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

Merge origin/master into copyNode

Conflicts:
	src/main/java/it/inaf/oats/vospace/JobService.java
	src/main/java/it/inaf/oats/vospace/MoveService.java
	src/test/java/it/inaf/oats/vospace/persistence/NodeDAOTest.java
Merge commit
parents a2faccb3 ecb8bc95
Branches
Tags
No related merge requests found
Showing
with 171 additions and 440 deletions
...@@ -3,10 +3,9 @@ ...@@ -3,10 +3,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>it.inaf.ia2</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>vospace-parent</artifactId>
<version>2.4.5</version> <version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>it.inaf.oats</groupId> <groupId>it.inaf.oats</groupId>
<artifactId>vospace-rest</artifactId> <artifactId>vospace-rest</artifactId>
...@@ -18,134 +17,19 @@ ...@@ -18,134 +17,19 @@
<!-- File catalog repository directory --> <!-- File catalog repository directory -->
<init_database_scripts_path>../../../vospace-file-catalog</init_database_scripts_path> <init_database_scripts_path>../../../vospace-file-catalog</init_database_scripts_path>
<finalName>${project.artifactId}-${project.version}</finalName> <finalName>${project.artifactId}-${project.version}</finalName>
<zonky.postgres-binaries.version>12.5.0</zonky.postgres-binaries.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Jackson-JAXB compatibility -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>it.oats.inaf</groupId>
<artifactId>vospace-datamodel</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<!-- Transitive dependency excluded to avoid duplicated dependency issues.
We want to use always the version provided by Spring Boot -->
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>it.inaf.ia2</groupId> <groupId>it.inaf.ia2</groupId>
<artifactId>auth-lib</artifactId> <artifactId>vospace-parent-classes</artifactId>
<version>2.0.0-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>redis.clients</groupId> <groupId>redis.clients</groupId>
<artifactId>jedis</artifactId> <artifactId>jedis</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
</dependency>
<!-- Embedded PostgreSQL: -->
<dependency>
<groupId>com.opentable.components</groupId>
<artifactId>otj-pg-embedded</artifactId>
<version>0.13.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>platform-linux</id>
<activation>
<os>
<family>unix</family>
</os>
</activation>
<dependencies>
<dependency>
<groupId>io.zonky.test.postgres</groupId>
<artifactId>embedded-postgres-binaries-linux-amd64</artifactId>
<version>${zonky.postgres-binaries.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>platform-windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<dependencies>
<dependency>
<groupId>io.zonky.test.postgres</groupId>
<artifactId>embedded-postgres-binaries-windows-amd64</artifactId>
<version>${zonky.postgres-binaries.version}</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</profile>
</profiles>
<repositories>
<repository>
<id>ia2-snapshots</id>
<name>your custom repo</name>
<url>http://repo.ia2.inaf.it/maven/repository/snapshots</url>
</repository>
</repositories>
<build> <build>
<finalName>${finalName}</finalName> <finalName>${finalName}</finalName>
...@@ -173,17 +57,9 @@ ...@@ -173,17 +57,9 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> </plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>
...@@ -202,4 +78,12 @@ ...@@ -202,4 +78,12 @@
</plugins> </plugins>
</build> </build>
<repositories>
<repository>
<id>ia2-snapshots</id>
<name>IA2 snapshot repository</name>
<url>http://repo.ia2.inaf.it/maven/repository/snapshots</url>
</repository>
</repositories>
</project> </project>
...@@ -70,7 +70,7 @@ public class CreateNodeService { ...@@ -70,7 +70,7 @@ public class CreateNodeService {
} }
if (!NodeUtils.checkIfWritable(parentNode, principal.getName(), principal.getGroups())) { if (!NodeUtils.checkIfWritable(parentNode, principal.getName(), principal.getGroups())) {
throw new PermissionDeniedException(path); throw PermissionDeniedException.forPath(path);
} }
// Check if node creator property is set. If not set it according to // Check if node creator property is set. If not set it according to
...@@ -87,7 +87,7 @@ public class CreateNodeService { ...@@ -87,7 +87,7 @@ public class CreateNodeService {
} else { } else {
if (!creator.equals(principal.getName())) // maybe a more specific exception would be more appropriate? if (!creator.equals(principal.getName())) // maybe a more specific exception would be more appropriate?
{ {
throw new PermissionDeniedException(path); throw PermissionDeniedException.forPath(path);
} }
} }
......
...@@ -55,7 +55,7 @@ public class DeleteNodeController extends BaseNodeController { ...@@ -55,7 +55,7 @@ public class DeleteNodeController extends BaseNodeController {
if (pathComponents.isEmpty()) { if (pathComponents.isEmpty()) {
// Manage root node // Manage root node
throw new PermissionDeniedException("root"); throw PermissionDeniedException.forPath("/");
} else { } else {
...@@ -72,7 +72,7 @@ public class DeleteNodeController extends BaseNodeController { ...@@ -72,7 +72,7 @@ public class DeleteNodeController extends BaseNodeController {
} }
if (!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) { if (!NodeUtils.checkIfWritable(toBeDeletedNode, principal.getName(), principal.getGroups())) {
throw new PermissionDeniedException(path); throw PermissionDeniedException.forPath(path);
} }
try { try {
......
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace;
import it.inaf.oats.vospace.exception.DefaultErrorController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ErrorController extends DefaultErrorController {
@Autowired
public ErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
}
...@@ -7,6 +7,8 @@ package it.inaf.oats.vospace; ...@@ -7,6 +7,8 @@ package it.inaf.oats.vospace;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import it.inaf.ia2.aa.data.User; import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.datamodel.Views;
import it.inaf.oats.vospace.exception.InvalidArgumentException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -39,8 +41,33 @@ public class FileServiceClient { ...@@ -39,8 +41,33 @@ public class FileServiceClient {
public String startArchiveJob(Transfer transfer, String jobId) { public String startArchiveJob(Transfer transfer, String jobId) {
List<String> vosPaths = transfer.getTarget().stream() if (transfer.getTarget().size() != 1) {
.map(p -> p.substring("vos://".length() + authority.length())).collect(Collectors.toList()); throw new IllegalArgumentException("Target size is " + transfer.getTarget().size());
}
String target = transfer.getTarget().get(0)
.substring("vos://".length() + authority.length());
String viewUri = transfer.getView().getUri();
// Generate list of paths using view include parameters
List<String> vosPaths = transfer.getView().getParam().stream()
.map(p -> {
if (p.getUri().equals(viewUri + "/include")) {
if (p.getValue().contains("../")) {
throw new InvalidArgumentException("Relative paths are not supported");
}
return target + "/" + p.getValue();
} else {
throw new InvalidArgumentException("Unsupported view parameter: " + p.getUri());
}
})
.collect(Collectors.toList());
if (vosPaths.isEmpty()) {
// Add target path
vosPaths.add(target);
}
ArchiveRequest archiveRequest = new ArchiveRequest(); ArchiveRequest archiveRequest = new ArchiveRequest();
archiveRequest.setJobId(jobId); archiveRequest.setJobId(jobId);
...@@ -65,7 +92,7 @@ public class FileServiceClient { ...@@ -65,7 +92,7 @@ public class FileServiceClient {
}, new Object[]{}); }, new Object[]{});
} }
private static class ArchiveRequest { public static class ArchiveRequest {
private String type; private String type;
private String jobId; private String jobId;
...@@ -98,9 +125,9 @@ public class FileServiceClient { ...@@ -98,9 +125,9 @@ public class FileServiceClient {
private static String archiveTypeFromViewUri(String viewUri) { private static String archiveTypeFromViewUri(String viewUri) {
switch (viewUri) { switch (viewUri) {
case "ivo://ia2.inaf.it/vospace/views#tar": case Views.TAR_VIEW_URI:
return "TAR"; return "TAR";
case "ivo://ia2.inaf.it/vospace/views#zip": case Views.ZIP_VIEW_URI:
return "ZIP"; return "ZIP";
default: default:
throw new IllegalArgumentException("Archive type not defined for " + viewUri); throw new IllegalArgumentException("Archive type not defined for " + viewUri);
......
...@@ -18,8 +18,9 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -18,8 +18,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException; import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Function;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.uws.v1.ResultReference;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -40,9 +41,6 @@ public class JobService { ...@@ -40,9 +41,6 @@ public class JobService {
@Autowired @Autowired
private CopyService copyService; private CopyService copyService;
@Autowired
private NodeBranchService nodeBranchService;
@Autowired @Autowired
private AsyncTransferService asyncTransfService; private AsyncTransferService asyncTransfService;
...@@ -106,15 +104,18 @@ public class JobService { ...@@ -106,15 +104,18 @@ public class JobService {
} }
job.setPhase(phase); job.setPhase(phase);
jobDAO.updateJob(job); jobDAO.updateJob(job, null);
Transfer negotiatedTransfer = null;
switch (getJobDirection(transfer)) { switch (getJobDirection(transfer)) {
case pullToVoSpace: case pullToVoSpace:
handlePullToVoSpace(job, transfer); negotiatedTransfer = handlePullToVoSpace(job, transfer);
break; break;
case pullFromVoSpace: case pullFromVoSpace:
case pushToVoSpace: case pushToVoSpace:
handleVoSpaceUrlsListResult(job, transfer); negotiatedTransfer = uriService.getNegotiatedTransfer(job, transfer);
setJobResults(job, transfer);
break; break;
case moveNode: case moveNode:
handleMoveNode(job, transfer); handleMoveNode(job, transfer);
...@@ -130,16 +131,18 @@ public class JobService { ...@@ -130,16 +131,18 @@ public class JobService {
// the previous job are asynchronous. Each job has to set its // the previous job are asynchronous. Each job has to set its
// completion independently. Only jobs started from the /synctrans // completion independently. Only jobs started from the /synctrans
// endpoints are completed immediately (see createSyncJobResult() method) // endpoints are completed immediately (see createSyncJobResult() method)
return negotiatedTransfer;
}); });
} }
private void handlePullToVoSpace(JobSummary job, Transfer transfer) { private Transfer handlePullToVoSpace(JobSummary job, Transfer transfer) {
for (Protocol protocol : transfer.getProtocols()) { for (Protocol protocol : transfer.getProtocols()) {
switch (protocol.getUri()) { switch (protocol.getUri()) {
case "ia2:async-recall": case "ia2:async-recall":
asyncTransfService.startJob(job); asyncTransfService.startJob(job);
return; return transfer;
case "ivo://ivoa.net/vospace/core#httpget": case "ivo://ivoa.net/vospace/core#httpget":
if (transfer.getTarget().size() != 1) { if (transfer.getTarget().size() != 1) {
throw new InvalidArgumentException("Invalid target size for pullToVoSpace: " + transfer.getTarget().size()); throw new InvalidArgumentException("Invalid target size for pullToVoSpace: " + transfer.getTarget().size());
...@@ -147,19 +150,18 @@ public class JobService { ...@@ -147,19 +150,18 @@ public class JobService {
String nodeUri = transfer.getTarget().get(0); String nodeUri = transfer.getTarget().get(0);
String contentUri = protocol.getEndpoint(); String contentUri = protocol.getEndpoint();
uriService.setNodeRemoteLocation(nodeUri, contentUri); uriService.setNodeRemoteLocation(nodeUri, contentUri);
uriService.setTransferJobResult(job, transfer); Transfer negotiatedTransfer = uriService.getNegotiatedTransfer(job, transfer);
setJobResults(job, transfer);
// Special case: import of a node from a portal file server // Special case: import of a node from a portal file server
// doesn't imply file transfer, so it can be set to completed // doesn't imply file transfer, so it can be set to completed
job.setPhase(ExecutionPhase.COMPLETED); job.setPhase(ExecutionPhase.COMPLETED);
return; return negotiatedTransfer;
default: default:
throw new InternalFaultException("Unsupported pullToVoSpace protocol: " + protocol.getUri()); throw new InvalidArgumentException("Unsupported pullToVoSpace protocol: " + protocol.getUri());
}
} }
} }
private void handleVoSpaceUrlsListResult(JobSummary job, Transfer transfer) { throw new InvalidArgumentException("Transfer contains no protocols");
uriService.setTransferJobResult(job, transfer);
} }
private void handleMoveNode(JobSummary jobSummary, Transfer transfer) { private void handleMoveNode(JobSummary jobSummary, Transfer transfer) {
...@@ -170,6 +172,7 @@ public class JobService { ...@@ -170,6 +172,7 @@ public class JobService {
handleJobErrors(jobSummary, job -> { handleJobErrors(jobSummary, job -> {
moveService.processMoveJob(transfer, user); moveService.processMoveJob(transfer, user);
job.setPhase(ExecutionPhase.COMPLETED); job.setPhase(ExecutionPhase.COMPLETED);
return null;
}); });
}); });
} }
...@@ -185,13 +188,16 @@ public class JobService { ...@@ -185,13 +188,16 @@ public class JobService {
// the file service part will unlock nodes and set job phase // the file service part will unlock nodes and set job phase
// to completed // to completed
return null;
}); });
}); });
} }
private void handleJobErrors(JobSummary job, Consumer<JobSummary> jobConsumer) { private void handleJobErrors(JobSummary job, Function<JobSummary, Transfer> jobConsumer) {
Transfer negotiatedTransfer = null;
try { try {
jobConsumer.accept(job); negotiatedTransfer = jobConsumer.apply(job);
} catch (VoSpaceErrorSummarizableException e) { } catch (VoSpaceErrorSummarizableException e) {
job.setPhase(ExecutionPhase.ERROR); job.setPhase(ExecutionPhase.ERROR);
job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e)); job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e));
...@@ -200,7 +206,7 @@ public class JobService { ...@@ -200,7 +206,7 @@ public class JobService {
job.setErrorSummary(ErrorSummaryFactory.newErrorSummary( job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(
new InternalFaultException(e))); new InternalFaultException(e)));
} finally { } finally {
jobDAO.updateJob(job); jobDAO.updateJob(job, negotiatedTransfer);
} }
} }
...@@ -217,21 +223,51 @@ public class JobService { ...@@ -217,21 +223,51 @@ public class JobService {
* *
*/ */
public void createSyncJobResult(JobSummary job) { public void createSyncJobResult(JobSummary job) {
Transfer negotiatedTransfer = null;
try { try {
uriService.setSyncTransferEndpoints(job); Transfer transfer = uriService.getTransfer(job);
negotiatedTransfer = uriService.getNegotiatedTransfer(job, transfer);
setJobResults(job, transfer);
job.setPhase(ExecutionPhase.COMPLETED); job.setPhase(ExecutionPhase.COMPLETED);
// Need to catch other exceptions too to avoid inconsistent job status // Need to catch other exceptions too to avoid inconsistent job status
} catch (VoSpaceErrorSummarizableException e) { } catch (VoSpaceErrorSummarizableException e) {
job.setPhase(ExecutionPhase.ERROR); job.setPhase(ExecutionPhase.ERROR);
uriService.getTransfer(job).getProtocols().clear(); stripProtocols(job, negotiatedTransfer);
job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e)); job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e));
} catch (Exception e) { } catch (Exception e) {
job.setPhase(ExecutionPhase.ERROR); job.setPhase(ExecutionPhase.ERROR);
uriService.getTransfer(job).getProtocols().clear(); stripProtocols(job, negotiatedTransfer);
job.setErrorSummary(ErrorSummaryFactory.newErrorSummary( job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(
new InternalFaultException(e))); new InternalFaultException(e)));
} finally { } finally {
jobDAO.createJob(job); jobDAO.createJob(job, negotiatedTransfer);
}
}
private void stripProtocols(JobSummary job, Transfer negotiatedTransfer) {
uriService.getTransfer(job).getProtocols().clear();
if (negotiatedTransfer != null) {
negotiatedTransfer.getProtocols().clear();
}
}
private void setJobResults(JobSummary jobSummary, Transfer transfer) {
String baseUrl = servletRequest.getRequestURL().substring(0,
servletRequest.getRequestURL().indexOf(servletRequest.getContextPath()));
String href = baseUrl + servletRequest.getContextPath()
+ "/transfers/" + jobSummary.getJobId() + "/results/transferDetails";
ResultReference transferDetailsRef = new ResultReference();
transferDetailsRef.setId("transferDetails");
transferDetailsRef.setHref(href);
jobSummary.getResults().add(transferDetailsRef);
switch (getJobDirection(transfer)) {
case pullFromVoSpace:
case pushToVoSpace:
ResultReference dataNodeRef = new ResultReference();
dataNodeRef.setId("dataNode");
dataNodeRef.setHref(transfer.getTarget().get(0));
jobSummary.getResults().add(dataNodeRef);
break;
} }
} }
} }
...@@ -47,7 +47,7 @@ public class ListNodeController extends BaseNodeController { ...@@ -47,7 +47,7 @@ public class ListNodeController extends BaseNodeController {
} else { } else {
if (!NodeUtils.checkIfReadable( if (!NodeUtils.checkIfReadable(
optNode.get(), principal.getName(), principal.getGroups())) { optNode.get(), principal.getName(), principal.getGroups())) {
throw new PermissionDeniedException(path); throw PermissionDeniedException.forPath(path);
} }
} }
......
...@@ -7,6 +7,7 @@ package it.inaf.oats.vospace; ...@@ -7,6 +7,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.NodeUtils; import it.inaf.oats.vospace.datamodel.NodeUtils;
import it.inaf.oats.vospace.exception.InternalFaultException;
import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.InvalidArgumentException;
import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.exception.NodeBusyException;
import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.exception.NodeNotFoundException;
...@@ -66,7 +67,7 @@ public class MoveService extends AbstractNodeService { ...@@ -66,7 +67,7 @@ public class MoveService extends AbstractNodeService {
} }
if (!nodeDao.isBranchWritable(sourceId, user.getName(), user.getGroups())) { if (!nodeDao.isBranchWritable(sourceId, user.getName(), user.getGroups())) {
throw new PermissionDeniedException(sourcePath); throw PermissionDeniedException.forPath(sourcePath);
} }
Optional<ShortNodeDescriptor> destShortNodeDescriptor Optional<ShortNodeDescriptor> destShortNodeDescriptor
...@@ -76,7 +77,12 @@ public class MoveService extends AbstractNodeService { ...@@ -76,7 +77,12 @@ public class MoveService extends AbstractNodeService {
if (destShortNodeDescriptor.isPresent()) { if (destShortNodeDescriptor.isPresent()) {
// When the destination is an existing ContainerNode, the source SHALL be placed under it (i.e., within the container) // When the destination is an existing ContainerNode, the source SHALL be placed under it (i.e., within the container)
ShortNodeDescriptor snd = destShortNodeDescriptor.get(); ShortNodeDescriptor snd = destShortNodeDescriptor.get();
this.validateDestinationContainer(snd, destinationPath);
if(snd.isBusy()) throw new NodeBusyException(destinationPath);
if(snd.isPermissionDenied()) throw PermissionDeniedException.forPath(destinationPath);
if(!snd.isWritable()) throw new InternalFaultException("Destination is not writable: "+ destinationPath);
if(!snd.isContainer()) throw new InternalFaultException("Existing destination is not a container: " + destinationPath);
destinationNodeLtreePath = snd.getDestinationNodeLtreePath(); destinationNodeLtreePath = snd.getDestinationNodeLtreePath();
} else { } else {
......
...@@ -50,7 +50,7 @@ public class SetNodeController extends BaseNodeController { ...@@ -50,7 +50,7 @@ public class SetNodeController extends BaseNodeController {
// The service SHALL throw a HTTP 403 status code including a PermissionDenied fault // 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 // in the entity-body if the user does not have permissions to perform the operation
if (!NodeUtils.checkIfWritable(toBeModifiedNode, principal.getName(), principal.getGroups())) { if (!NodeUtils.checkIfWritable(toBeModifiedNode, principal.getName(), principal.getGroups())) {
throw new PermissionDeniedException(path); throw PermissionDeniedException.forPath(path);
} }
// The service SHALL throw a HTTP 403 status code including a PermissionDenied fault // The service SHALL throw a HTTP 403 status code including a PermissionDenied fault
...@@ -60,7 +60,7 @@ public class SetNodeController extends BaseNodeController { ...@@ -60,7 +60,7 @@ public class SetNodeController extends BaseNodeController {
String newNodeType = node.getType(); String newNodeType = node.getType();
if (!storedNodeType.equals(newNodeType)) { if (!storedNodeType.equals(newNodeType)) {
LOG.debug("setNode trying to modify type. Stored ", storedNodeType + ", requested " + newNodeType); LOG.debug("setNode trying to modify type. Stored ", storedNodeType + ", requested " + newNodeType);
throw new PermissionDeniedException(path); throw PermissionDeniedException.forPath(path);
} }
// This method cannot be used to modify the accepts or provides list of Views for the Node. // This method cannot be used to modify the accepts or provides list of Views for the Node.
......
...@@ -53,7 +53,7 @@ public class TransferController { ...@@ -53,7 +53,7 @@ public class TransferController {
JobSummary jobSummary = newJobSummary(transfer, principal); JobSummary jobSummary = newJobSummary(transfer, principal);
jobDAO.createJob(jobSummary); jobDAO.createJob(jobSummary, null);
if (phase.isPresent()) { if (phase.isPresent()) {
jobService.setJobPhase(jobSummary, phase.get()); jobService.setJobPhase(jobSummary, phase.get());
...@@ -157,8 +157,7 @@ public class TransferController { ...@@ -157,8 +157,7 @@ public class TransferController {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} }
// TODO: check type return ResponseEntity.ok(jobDAO.getTransferDetails(jobId));
return ResponseEntity.ok((Transfer) (job.getJobInfo().getAny().get(0)));
}).orElse(ResponseEntity.notFound().build()); }).orElse(ResponseEntity.notFound().build());
} }
...@@ -174,6 +173,7 @@ public class TransferController { ...@@ -174,6 +173,7 @@ public class TransferController {
@RequestParam(value = "PHASE", required = false) Optional<List<ExecutionPhase>> phase, @RequestParam(value = "PHASE", required = false) Optional<List<ExecutionPhase>> phase,
@RequestParam(value = "AFTER", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Optional<LocalDateTime> after, @RequestParam(value = "AFTER", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Optional<LocalDateTime> after,
@RequestParam(value = "LAST", required = false) Optional<Integer> last, @RequestParam(value = "LAST", required = false) Optional<Integer> last,
@RequestParam(value = "VIEW", required = false) Optional<List<String>> views,
@RequestParam(value = "direction", required = false) Optional<List<JobService.JobDirection>> direction, @RequestParam(value = "direction", required = false) Optional<List<JobService.JobDirection>> direction,
User principal) { User principal) {
...@@ -185,21 +185,11 @@ public class TransferController { ...@@ -185,21 +185,11 @@ public class TransferController {
String userId = principal.getName(); String userId = principal.getName();
List<ExecutionPhase> phaseList; List<ExecutionPhase> phaseList = phase.orElse(List.of());
if (phase.isPresent()) { List<JobService.JobDirection> directionList = direction.orElse(List.of());
phaseList = phase.get(); List<String> viewsList = views.orElse(List.of());
} else {
phaseList = List.of();
}
List<JobService.JobDirection> directionList;
if (direction.isPresent()) {
directionList = direction.get();
} else {
directionList = List.of();
}
Jobs jobs = jobDAO.getJobs(userId, phaseList, directionList, after, last); Jobs jobs = jobDAO.getJobs(userId, phaseList, directionList, viewsList, after, last);
return ResponseEntity.ok(jobs); return ResponseEntity.ok(jobs);
} }
......
...@@ -31,7 +31,6 @@ import java.util.Optional; ...@@ -31,7 +31,6 @@ import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
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.vospace.v2.DataNode; import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.Node; import net.ivoa.xml.vospace.v2.Node;
import net.ivoa.xml.vospace.v2.Protocol; import net.ivoa.xml.vospace.v2.Protocol;
...@@ -67,24 +66,17 @@ public class UriService { ...@@ -67,24 +66,17 @@ public class UriService {
@Autowired @Autowired
private FileServiceClient fileServiceClient; private FileServiceClient fileServiceClient;
public void setTransferJobResult(JobSummary job, Transfer transfer) {
List<ResultReference> results = new ArrayList<>();
ResultReference result = new ResultReference();
result.setHref(getEndpoint(job, transfer));
results.add(result);
job.setResults(results);
// Moved phase setting to caller method for ERROR management
}
/** /**
* Sets the endpoint value for all valid protocols (protocol negotiation). * For a given job, returns a new transfer object containing only valid
* protocols (protocol negotiation) and sets proper endpoints on them.
*/ */
public void setSyncTransferEndpoints(JobSummary job) { public Transfer getNegotiatedTransfer(JobSummary job, Transfer transfer) {
Transfer transfer = getTransfer(job); // Original transfer object shouldn't be modified, so a new transfer object is created
Transfer negotiatedTransfer = new Transfer();
negotiatedTransfer.setTarget(transfer.getTarget());
negotiatedTransfer.setDirection(transfer.getDirection());
// according to examples found in specification view is not copied
if (transfer.getProtocols().isEmpty()) { if (transfer.getProtocols().isEmpty()) {
// At least one protocol is expected from client // At least one protocol is expected from client
...@@ -97,6 +89,7 @@ public class UriService { ...@@ -97,6 +89,7 @@ public class UriService {
List<String> validProtocolUris = new ArrayList<>(); List<String> validProtocolUris = new ArrayList<>();
switch (jobDirection) { switch (jobDirection) {
case pullFromVoSpace: case pullFromVoSpace:
case pullToVoSpace:
validProtocolUris.add("ivo://ivoa.net/vospace/core#httpget"); validProtocolUris.add("ivo://ivoa.net/vospace/core#httpget");
break; break;
case pushToVoSpace: case pushToVoSpace:
...@@ -109,20 +102,24 @@ public class UriService { ...@@ -109,20 +102,24 @@ public class UriService {
List<Protocol> validProtocols List<Protocol> validProtocols
= transfer.getProtocols().stream() = transfer.getProtocols().stream()
// discard invalid protocols
.filter(protocol -> validProtocolUris.contains(protocol.getUri())) .filter(protocol -> validProtocolUris.contains(protocol.getUri()))
.collect(Collectors.toList()); .map(p -> {
// set endpoints
Protocol protocol = new Protocol();
protocol.setUri(p.getUri());
protocol.setEndpoint(getEndpoint(job, transfer));
return protocol;
}).collect(Collectors.toList());
if (validProtocols.isEmpty()) { if (validProtocols.isEmpty()) {
Protocol protocol = transfer.getProtocols().get(0); Protocol protocol = transfer.getProtocols().get(0);
throw new ProtocolNotSupportedException(protocol.getUri()); throw new ProtocolNotSupportedException(protocol.getUri());
} }
String endpoint = getEndpoint(job, transfer); negotiatedTransfer.getProtocols().addAll(validProtocols);
validProtocols.stream().forEach(p -> p.setEndpoint(endpoint));
// Returns modified transfer containing only valid protocols return negotiatedTransfer;
transfer.getProtocols().clear();
transfer.getProtocols().addAll(validProtocols);
} }
private Node getEndpointNode(String relativePath, private Node getEndpointNode(String relativePath,
...@@ -168,13 +165,13 @@ public class UriService { ...@@ -168,13 +165,13 @@ public class UriService {
case pushToVoSpace: case pushToVoSpace:
case pullToVoSpace: case pullToVoSpace:
if (!NodeUtils.checkIfWritable(node, creator, groups)) { if (!NodeUtils.checkIfWritable(node, creator, groups)) {
throw new PermissionDeniedException(relativePath); throw PermissionDeniedException.forPath(relativePath);
} }
break; break;
case pullFromVoSpace: case pullFromVoSpace:
if (!NodeUtils.checkIfReadable(node, creator, groups)) { if (!NodeUtils.checkIfReadable(node, creator, groups)) {
throw new PermissionDeniedException(relativePath); throw PermissionDeniedException.forPath(relativePath);
} }
break; break;
......
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ContainerNotFoundException extends VoSpaceErrorSummarizableException {
public ContainerNotFoundException(String path) {
super("Path: " + path,
VOSpaceFaultEnum.NODE_NOT_FOUND);
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.CONFLICT)
public class DuplicateNodeException extends VoSpaceErrorSummarizableException {
public DuplicateNodeException(String path) {
super("Path: " + path,
VOSpaceFaultEnum.DUPLICATE_NODE);
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("${server.error.path:${error.path:/error}}")
public class ErrorController extends AbstractErrorController {
@Autowired
public ErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@RequestMapping(produces = MediaType.TEXT_XML_VALUE)
public void errorText(HttpServletRequest request, HttpServletResponse response) throws Exception {
ErrorAttributeOptions options = ErrorAttributeOptions.of(ErrorAttributeOptions.Include.MESSAGE);
Map<String, Object> errors = super.getErrorAttributes(request, options);
response.setContentType("text/plain;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
String errorMessage = (String) errors.get("message");
if (errorMessage != null) {
response.getOutputStream().write(errorMessage.getBytes(StandardCharsets.UTF_8));
}
}
@Override
public String getErrorPath() {
return null;
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import net.ivoa.xml.uws.v1.ErrorSummary;
public class ErrorSummaryFactory {
public static ErrorSummary newErrorSummary(VOSpaceFaultEnum error, String detailMessage) {
ErrorSummary result = new ErrorSummary();
result.setMessage(error.getFaultRepresentation());
result.setType(error.getType());
if (detailMessage == null || detailMessage.isBlank()) {
result.setHasDetail(false);
} else {
result.setHasDetail(true);
result.setDetailMessage(error.getFaultCaptionForDetails()
+ " "
+ detailMessage);
}
return result;
}
public static ErrorSummary newErrorSummary(VOSpaceFaultEnum error) {
return newErrorSummary(error, null);
}
public static ErrorSummary newErrorSummary(VoSpaceErrorSummarizableException e)
{
return newErrorSummary(e.getFault(), e.getDetailMessage());
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) // Status code 500
public class InternalFaultException extends VoSpaceErrorSummarizableException {
private static final Logger LOG = LoggerFactory.getLogger(InternalFaultException.class);
public InternalFaultException(String msg) {
super("Description: " + msg,
VOSpaceFaultEnum.INTERNAL_FAULT);
}
public InternalFaultException(Throwable cause) {
super("Description: " + getMessage(cause),
VOSpaceFaultEnum.INTERNAL_FAULT);
}
private static String getMessage(Throwable cause) {
LOG.error("Exception caught", cause);
return cause.getMessage() != null ? cause.getMessage() : cause.getClass().getCanonicalName();
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class InvalidArgumentException extends VoSpaceErrorSummarizableException {
public InvalidArgumentException(String message) {
super("Description: " + message, VOSpaceFaultEnum.NODE_NOT_FOUND);
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class InvalidURIException extends VoSpaceErrorSummarizableException {
public InvalidURIException(String URI, String path) {
super("Payload node URI: " + URI
+ " is not consistent with request path: " + path,
VOSpaceFaultEnum.INVALID_URI);
}
public InvalidURIException(String URI) {
super("URI: " + URI + " is not in a valid format",
VOSpaceFaultEnum.INVALID_URI);
}
public InvalidURIException(IllegalArgumentException ex) {
super("Description: " + ex.getMessage(),
VOSpaceFaultEnum.INVALID_URI);
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class LinkFoundException extends VoSpaceErrorSummarizableException {
public LinkFoundException(String path) {
super("Link Node found at path: " + path,
VOSpaceFaultEnum.INVALID_URI);
}
}
/*
* This file is part of vospace-rest
* Copyright (C) 2021 Istituto Nazionale di Astrofisica
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package it.inaf.oats.vospace.exception;
public class NodeBusyException extends VoSpaceErrorSummarizableException {
public NodeBusyException(String path) {
super("Path: " + path,
VOSpaceFaultEnum.NODE_BUSY);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment