From b0f23cd81dc760821c226ee03036c76ea45478fb Mon Sep 17 00:00:00 2001 From: Nicola Fulvio Calabria <nicola.calabria@inaf.it> Date: Thu, 8 Apr 2021 10:27:55 +0200 Subject: [PATCH] Task #3637 - Added Fault management for transfer services up to 3/6 redmine subtasks --- .../java/it/inaf/oats/vospace/JobService.java | 75 +++++++++++++------ .../inaf/oats/vospace/TransferController.java | 17 +++-- .../java/it/inaf/oats/vospace/UriService.java | 49 ++++++++++-- .../exception/DuplicateNodeException.java | 6 +- .../exception/InternalFaultException.java | 9 ++- .../exception/InvalidURIException.java | 12 ++- .../vospace/exception/NodeBusyException.java | 12 +++ .../exception/NodeNotFoundException.java | 6 +- .../exception/PermissionDeniedException.java | 6 +- .../ProtocolNotSupportedException.java | 12 +++ .../VoSpaceErrorSummarizableException.java | 23 ++++++ .../inaf/oats/vospace/persistence/JobDAO.java | 61 ++++++++++++--- .../oats/vospace/TransferControllerTest.java | 13 ++++ .../it/inaf/oats/vospace/UriServiceTest.java | 14 +++- 14 files changed, 255 insertions(+), 60 deletions(-) create mode 100644 src/main/java/it/inaf/oats/vospace/exception/NodeBusyException.java create mode 100644 src/main/java/it/inaf/oats/vospace/exception/ProtocolNotSupportedException.java create mode 100644 src/main/java/it/inaf/oats/vospace/exception/VoSpaceErrorSummarizableException.java diff --git a/src/main/java/it/inaf/oats/vospace/JobService.java b/src/main/java/it/inaf/oats/vospace/JobService.java index 95e11ad..291d9eb 100644 --- a/src/main/java/it/inaf/oats/vospace/JobService.java +++ b/src/main/java/it/inaf/oats/vospace/JobService.java @@ -6,8 +6,10 @@ import net.ivoa.xml.uws.v1.ExecutionPhase; 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.uws.v1.ErrorSummaryFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import it.inaf.oats.vospace.exception.VoSpaceErrorSummarizableException; @Service public class JobService { @@ -46,7 +48,7 @@ public class JobService { private void startJob(JobSummary job) { Transfer transfer = uriService.getTransfer(job); - + switch (getJobType(transfer)) { case pullToVoSpace: handlePullToVoSpace(job, transfer); @@ -61,29 +63,44 @@ public class JobService { } private void handlePullToVoSpace(JobSummary job, Transfer transfer) { - - for (Protocol protocol : transfer.getProtocols()) { - switch (protocol.getUri()) { - case "ia2:async-recall": - 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()); + try { + for (Protocol protocol : transfer.getProtocols()) { + switch (protocol.getUri()) { + case "ia2:async-recall": + asyncTransfService.startJob(job); + // ASK IF IT's OK neglect phase update. + return; + case "ivo://ivoa.net/vospace/core#httpget": + String nodeUri = transfer.getTarget(); + String contentUri = protocol.getEndpoint(); + uriService.setNodeRemoteLocation(nodeUri, contentUri); + uriService.setTransferJobResult(job, transfer); + job.setPhase(ExecutionPhase.COMPLETED); + jobDAO.updateJob(job); + return; + default: + throw new InternalFaultException("Unsupported pullToVoSpace protocol: " + protocol.getUri()); + } } + } catch (VoSpaceErrorSummarizableException e) { + job.setPhase(ExecutionPhase.ERROR); + job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e.getFault())); + jobDAO.updateJob(job); } } private void handleVoSpaceUrlsListResult(JobSummary job, Transfer transfer) { - job.setPhase(ExecutionPhase.EXECUTING); - uriService.setTransferJobResult(job, transfer); - jobDAO.updateJob(job); + try { + job.setPhase(ExecutionPhase.EXECUTING); + uriService.setTransferJobResult(job, transfer); + job.setPhase(ExecutionPhase.COMPLETED); + // Need to catch other exceptions too to avoid inconsistent job status + } catch (VoSpaceErrorSummarizableException e) { + job.setPhase(ExecutionPhase.ERROR); + job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e.getFault())); + } finally { + jobDAO.updateJob(job); + } } private JobType getJobType(Transfer transfer) { @@ -92,11 +109,23 @@ public class JobService { /** * Synchronous transfer endpoint creates a job that is immediately set to - * completed. + * COMPLETED or to ERROR in case some fault occurred. + * + * In case of ERROR, Protocols are stripped from job representation in + * compliance with specifications + * */ public void createSyncJobResult(JobSummary job) { - job.setPhase(ExecutionPhase.COMPLETED); - uriService.setSyncTransferEndpoints(job); - jobDAO.createJob(job); + try { + uriService.setSyncTransferEndpoints(job); + job.setPhase(ExecutionPhase.COMPLETED); + // Need to catch other exceptions too to avoid inconsistent job status + } catch (VoSpaceErrorSummarizableException e) { + job.setPhase(ExecutionPhase.ERROR); + uriService.getTransfer(job).getProtocols().clear(); + job.setErrorSummary(ErrorSummaryFactory.newErrorSummary(e.getFault())); + } finally { + jobDAO.createJob(job); + } } } diff --git a/src/main/java/it/inaf/oats/vospace/TransferController.java b/src/main/java/it/inaf/oats/vospace/TransferController.java index d694f94..5169f98 100644 --- a/src/main/java/it/inaf/oats/vospace/TransferController.java +++ b/src/main/java/it/inaf/oats/vospace/TransferController.java @@ -1,6 +1,7 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.data.User; +import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.persistence.JobDAO; import java.util.Optional; import java.util.UUID; @@ -9,6 +10,7 @@ import net.ivoa.xml.uws.v1.ExecutionPhase; import net.ivoa.xml.uws.v1.JobSummary; import net.ivoa.xml.uws.v1.Jobs; import net.ivoa.xml.vospace.v2.Transfer; +import net.ivoa.xml.vospace.v2.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -23,6 +25,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; import java.util.List; +import java.util.stream.Collectors; @RestController public class TransferController { @@ -113,12 +116,12 @@ public class TransferController { @RequestParam(value = "AFTER", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Optional<LocalDateTime> after, @RequestParam(value = "LAST", required = false) Optional<Integer> last, @RequestParam(value = "direction", required = false) Optional<List<JobService.JobType>> direction, - User principal) { - - if(last.isPresent()) - { - if(last.get() <= 0) + User principal) { + + if (last.isPresent()) { + if (last.get() <= 0) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); + } } String userId = principal.getName(); @@ -135,7 +138,7 @@ public class TransferController { directionList = direction.get(); } else { directionList = List.of(); - } + } Jobs jobs = jobDAO.getJobs(userId, phaseList, directionList, after, last); @@ -144,7 +147,7 @@ public class TransferController { private JobSummary newJobSummary(Transfer transfer, User principal) { String jobId = UUID.randomUUID().toString().replace("-", ""); - + JobSummary jobSummary = new JobSummary(); jobSummary.setJobId(jobId); jobSummary.setOwnerId(principal.getName()); diff --git a/src/main/java/it/inaf/oats/vospace/UriService.java b/src/main/java/it/inaf/oats/vospace/UriService.java index b7d132a..ec64978 100644 --- a/src/main/java/it/inaf/oats/vospace/UriService.java +++ b/src/main/java/it/inaf/oats/vospace/UriService.java @@ -3,11 +3,15 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.ServletRapClient; import it.inaf.ia2.aa.data.User; import it.inaf.ia2.rap.client.call.TokenExchangeRequest; +import it.inaf.oats.vospace.JobService.JobType; import it.inaf.oats.vospace.datamodel.NodeProperties; +import it.inaf.oats.vospace.datamodel.NodeUtils; 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.PermissionDeniedException; +import it.inaf.oats.vospace.exception.ProtocolNotSupportedException; +import it.inaf.oats.vospace.exception.NodeBusyException; import it.inaf.oats.vospace.persistence.LocationDAO; import it.inaf.oats.vospace.persistence.NodeDAO; import it.inaf.oats.vospace.persistence.model.Location; @@ -15,12 +19,14 @@ import it.inaf.oats.vospace.persistence.model.LocationType; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Optional; 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.ResultReference; import net.ivoa.xml.vospace.v2.Node; +import net.ivoa.xml.vospace.v2.Param; import net.ivoa.xml.vospace.v2.Protocol; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; @@ -38,7 +44,7 @@ public class UriService { @Autowired private NodeDAO nodeDao; - + @Autowired private LocationDAO locationDAO; @@ -52,12 +58,13 @@ public class UriService { List<ResultReference> results = new ArrayList<>(); - ResultReference result = new ResultReference(); - result.setHref(getEndpoint(job, transfer)); + ResultReference result = new ResultReference(); + result.setHref(getEndpoint(job, transfer)); results.add(result); job.setResults(results); - job.setPhase(ExecutionPhase.COMPLETED); + // Moved phase setting to caller method for ERROR management + // job.setPhase(ExecutionPhase.COMPLETED); } public void setSyncTransferEndpoints(JobSummary job) { @@ -68,7 +75,8 @@ public class UriService { if (!"ivo://ivoa.net/vospace/core#httpget".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()); + throw new ProtocolNotSupportedException(protocol.getUri()); } protocol.setEndpoint(getEndpoint(job, transfer)); } @@ -78,6 +86,35 @@ public class UriService { String relativePath = transfer.getTarget().substring("vos://".length() + authority.length()); Node node = nodeDao.listNode(relativePath).orElseThrow(() -> new NodeNotFoundException(relativePath)); + + User user = (User) servletRequest.getUserPrincipal(); + String creator = user.getName(); + List<String> groups = user.getGroups(); + + // Check privileges write or read according to job type + JobService.JobType jobType = JobType.valueOf(transfer.getDirection()); + + switch (jobType) { + case pushToVoSpace: + case pullToVoSpace: + if (!NodeUtils.checkIfWritable(node, creator, groups)) { + throw new PermissionDeniedException(relativePath); + } + break; + + case pullFromVoSpace: + if (!NodeUtils.checkIfReadable(node, creator, groups)) { + throw new PermissionDeniedException(relativePath); + } + break; + + default: + throw new InternalFaultException("No job direction specified"); + } + + if (NodeUtils.getIsBusy(node)) { + throw new NodeBusyException(relativePath); + } Location location = locationDAO.getNodeLocation(relativePath).orElse(null); diff --git a/src/main/java/it/inaf/oats/vospace/exception/DuplicateNodeException.java b/src/main/java/it/inaf/oats/vospace/exception/DuplicateNodeException.java index a64223e..0b09d37 100644 --- a/src/main/java/it/inaf/oats/vospace/exception/DuplicateNodeException.java +++ b/src/main/java/it/inaf/oats/vospace/exception/DuplicateNodeException.java @@ -1,12 +1,14 @@ package it.inaf.oats.vospace.exception; +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value = HttpStatus.CONFLICT) -public class DuplicateNodeException extends VoSpaceException { +public class DuplicateNodeException extends VoSpaceErrorSummarizableException { public DuplicateNodeException(String path) { - super("Duplicate Node at path: " + path); + super("Duplicate Node at path: " + path, + ErrorSummaryFactory.VOSpaceFault.DUPLICATE_NODE); } } diff --git a/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java b/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java index 635bf57..05d702e 100644 --- a/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java +++ b/src/main/java/it/inaf/oats/vospace/exception/InternalFaultException.java @@ -1,21 +1,24 @@ package it.inaf.oats.vospace.exception; +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; 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 VoSpaceException { +public class InternalFaultException extends VoSpaceErrorSummarizableException { private static final Logger LOG = LoggerFactory.getLogger(InternalFaultException.class); public InternalFaultException(String msg) { - super("InternalFaultException: " + msg); + super("InternalFaultException: " + msg, + ErrorSummaryFactory.VOSpaceFault.INTERNAL_FAULT); } public InternalFaultException(Throwable cause) { - super("InternalFaultException: " + getMessage(cause)); + super("InternalFaultException: " + getMessage(cause), + ErrorSummaryFactory.VOSpaceFault.INTERNAL_FAULT); } private static String getMessage(Throwable cause) { diff --git a/src/main/java/it/inaf/oats/vospace/exception/InvalidURIException.java b/src/main/java/it/inaf/oats/vospace/exception/InvalidURIException.java index 085d102..e91e3f5 100644 --- a/src/main/java/it/inaf/oats/vospace/exception/InvalidURIException.java +++ b/src/main/java/it/inaf/oats/vospace/exception/InvalidURIException.java @@ -1,21 +1,25 @@ package it.inaf.oats.vospace.exception; +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value = HttpStatus.BAD_REQUEST) -public class InvalidURIException extends VoSpaceException { +public class InvalidURIException extends VoSpaceErrorSummarizableException { public InvalidURIException(String URI, String path) { super("InvalidURI. Payload node URI: " + URI - + " is not consistent with request path: " + path); + + " is not consistent with request path: " + path, + ErrorSummaryFactory.VOSpaceFault.INVALID_URI); } public InvalidURIException(String URI) { - super("InvalidURI. URI: " + URI + " is not in a valid format"); + super("InvalidURI. URI: " + URI + " is not in a valid format", + ErrorSummaryFactory.VOSpaceFault.INVALID_URI); } public InvalidURIException(IllegalArgumentException ex) { - super("InvalidURI. " + ex.getMessage()); + super("InvalidURI. " + ex.getMessage(), + ErrorSummaryFactory.VOSpaceFault.INVALID_URI); } } diff --git a/src/main/java/it/inaf/oats/vospace/exception/NodeBusyException.java b/src/main/java/it/inaf/oats/vospace/exception/NodeBusyException.java new file mode 100644 index 0000000..2586ac6 --- /dev/null +++ b/src/main/java/it/inaf/oats/vospace/exception/NodeBusyException.java @@ -0,0 +1,12 @@ +package it.inaf.oats.vospace.exception; + +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; + +public class NodeBusyException extends VoSpaceErrorSummarizableException { + + public NodeBusyException(String path) { + super("Node Busy: at path " + path, + ErrorSummaryFactory.VOSpaceFault.NODE_BUSY); + } + +} diff --git a/src/main/java/it/inaf/oats/vospace/exception/NodeNotFoundException.java b/src/main/java/it/inaf/oats/vospace/exception/NodeNotFoundException.java index b14f4c2..a7bb797 100644 --- a/src/main/java/it/inaf/oats/vospace/exception/NodeNotFoundException.java +++ b/src/main/java/it/inaf/oats/vospace/exception/NodeNotFoundException.java @@ -1,12 +1,14 @@ package it.inaf.oats.vospace.exception; +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value = HttpStatus.NOT_FOUND) -public class NodeNotFoundException extends VoSpaceException { +public class NodeNotFoundException extends VoSpaceErrorSummarizableException { public NodeNotFoundException(String path) { - super("NodeNotFound: " + path); + super("NodeNotFound: " + path, + ErrorSummaryFactory.VOSpaceFault.NODE_NOT_FOUND); } } diff --git a/src/main/java/it/inaf/oats/vospace/exception/PermissionDeniedException.java b/src/main/java/it/inaf/oats/vospace/exception/PermissionDeniedException.java index 20a1991..31194db 100644 --- a/src/main/java/it/inaf/oats/vospace/exception/PermissionDeniedException.java +++ b/src/main/java/it/inaf/oats/vospace/exception/PermissionDeniedException.java @@ -1,12 +1,14 @@ package it.inaf.oats.vospace.exception; +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value = HttpStatus.FORBIDDEN) -public class PermissionDeniedException extends VoSpaceException { +public class PermissionDeniedException extends VoSpaceErrorSummarizableException { public PermissionDeniedException(String path) { - super("Permission Denied at path: " + path); + super("Permission Denied at path: " + path, + ErrorSummaryFactory.VOSpaceFault.PERMISSION_DENIED); } } diff --git a/src/main/java/it/inaf/oats/vospace/exception/ProtocolNotSupportedException.java b/src/main/java/it/inaf/oats/vospace/exception/ProtocolNotSupportedException.java new file mode 100644 index 0000000..d13d941 --- /dev/null +++ b/src/main/java/it/inaf/oats/vospace/exception/ProtocolNotSupportedException.java @@ -0,0 +1,12 @@ +package it.inaf.oats.vospace.exception; + +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; + +public class ProtocolNotSupportedException extends VoSpaceErrorSummarizableException{ + + public ProtocolNotSupportedException(String protocol) { + super("Protocol Not Supported: " + protocol, + ErrorSummaryFactory.VOSpaceFault.PROTOCOL_NOT_SUPPORTED); + } + +} diff --git a/src/main/java/it/inaf/oats/vospace/exception/VoSpaceErrorSummarizableException.java b/src/main/java/it/inaf/oats/vospace/exception/VoSpaceErrorSummarizableException.java new file mode 100644 index 0000000..caed42e --- /dev/null +++ b/src/main/java/it/inaf/oats/vospace/exception/VoSpaceErrorSummarizableException.java @@ -0,0 +1,23 @@ +package it.inaf.oats.vospace.exception; + +import net.ivoa.xml.uws.v1.ErrorSummaryFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) +public class VoSpaceErrorSummarizableException extends VoSpaceException { + + ErrorSummaryFactory.VOSpaceFault fault; + + public VoSpaceErrorSummarizableException(String message, + ErrorSummaryFactory.VOSpaceFault fault) + { + super(message); + this.fault = fault; + } + + public ErrorSummaryFactory.VOSpaceFault getFault() + { + return this.fault; + } +} diff --git a/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java b/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java index 0fee2b1..37657b0 100644 --- a/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java +++ b/src/main/java/it/inaf/oats/vospace/persistence/JobDAO.java @@ -3,6 +3,7 @@ package it.inaf.oats.vospace.persistence; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; +import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; import java.sql.Timestamp; import java.sql.ResultSet; import java.sql.SQLException; @@ -14,6 +15,7 @@ import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.XMLGregorianCalendar; import net.ivoa.xml.uws.v1.ExecutionPhase; import net.ivoa.xml.uws.v1.JobSummary; +import net.ivoa.xml.uws.v1.ErrorSummary; import net.ivoa.xml.uws.v1.ShortJobDescription; import net.ivoa.xml.uws.v1.ResultReference; import net.ivoa.xml.uws.v1.Jobs; @@ -25,6 +27,7 @@ import org.springframework.stereotype.Repository; import java.util.ArrayList; import java.time.LocalDateTime; import java.math.BigDecimal; +import net.ivoa.xml.uws.v1.ErrorType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,7 +35,7 @@ import org.slf4j.LoggerFactory; public class JobDAO { private static final Logger LOG = LoggerFactory.getLogger(JobDAO.class); - + private static final ObjectMapper MAPPER = new ObjectMapper(); private final JdbcTemplate jdbcTemplate; @@ -44,7 +47,10 @@ public class JobDAO { public void createJob(JobSummary jobSummary) { - String sql = "INSERT INTO job(job_id, owner_id, job_type, phase, job_info) VALUES (?, ?, ?, ?, ?)"; + String sql = + "INSERT INTO job(job_id, owner_id, job_type, phase, job_info," + + " error_message, error_type, error_has_detail) " + + "VALUES (?, ?, ?, ?, ?, ? ,? ,?)"; jdbcTemplate.update(sql, ps -> { int i = 0; @@ -53,6 +59,17 @@ public class JobDAO { ps.setObject(++i, getJobType(jobSummary), Types.OTHER); ps.setObject(++i, jobSummary.getPhase().value(), Types.OTHER); ps.setObject(++i, toJson(jobSummary.getJobInfo()), Types.OTHER); + + ErrorSummary errorSummary = jobSummary.getErrorSummary(); + if(errorSummary != null) { + ps.setString(++i, errorSummary.getMessage()); + ps.setObject(++i, errorSummary.getType().value(), Types.OTHER); + ps.setBoolean(++i, errorSummary.isHasDetail()); + } else { + ps.setNull(++i, Types.VARCHAR); + ps.setNull(++i, Types.OTHER); + ps.setNull(++i, Types.BOOLEAN); + } }); } @@ -102,6 +119,16 @@ public class JobDAO { jobSummary.setPhase(ExecutionPhase.fromValue(rs.getString("phase"))); jobSummary.setJobInfo(getJobPayload(rs.getString("job_info"))); jobSummary.setResults(getResults(rs.getString("results"))); + + // Retrieve error information if any + ErrorSummary errorSummary = new ErrorSummary(); + errorSummary.setMessage(rs.getString("error_message")); + String errorType = rs.getString("error_type"); + if (errorType != null) { + errorSummary.setType(ErrorType.fromValue(rs.getString("error_type"))); + } + errorSummary.setHasDetail(rs.getBoolean("error_has_detail")); + jobSummary.setErrorSummary(errorSummary); return jobSummary; } @@ -133,9 +160,9 @@ public class JobDAO { queryParams.add(ExecutionPhase.ARCHIVED); queryParamTypes.add(Types.OTHER); } else { - sb.append(" AND phase IN ("); + sb.append(" AND phase IN ("); for (int i = 0; i < phaseList.size(); i++) { - sb.append("?"); + sb.append("?"); queryParams.add(phaseList.get(i)); queryParamTypes.add(Types.OTHER); if (i < phaseList.size() - 1) { @@ -179,14 +206,14 @@ public class JobDAO { // Perform query jdbcTemplate.query(sql, - (ps) -> { + (ps) -> { for (int i = 0; i < queryParams.size(); i++) { ps.setObject(i + 1, queryParams.get(i), queryParamTypes.get(i)); } }, - (rs) -> { - sjdList.add(getShortJobDescriptionFromCurrentRow(rs)); + (rs) -> { + sjdList.add(getShortJobDescriptionFromCurrentRow(rs)); } ); @@ -201,8 +228,8 @@ public class JobDAO { sjd.setType(rs.getString("job_type")); sjd.setPhase(ExecutionPhase.fromValue(rs.getString("phase"))); sjd.setCreationTime( - toXMLGregorianCalendar(rs.getTimestamp("creation_time"))); - + toXMLGregorianCalendar(rs.getTimestamp("creation_time"))); + return sjd; } @@ -228,13 +255,27 @@ public class JobDAO { public void updateJob(JobSummary job) { - String sql = "UPDATE job SET phase = ?, results = ? WHERE job_id = ?"; + String sql = "UPDATE job SET phase = ?, results = ? "; + + ErrorSummary errorSummary = job.getErrorSummary(); + if(errorSummary != null) + { + sql += ", error_message = ?, error_type = ?, error_has_detail = ? "; + } + + sql += "WHERE job_id = ?"; jdbcTemplate.update(sql, ps -> { int i = 0; ps.setObject(++i, job.getPhase().name(), Types.OTHER); ps.setObject(++i, toJson(job.getResults()), Types.OTHER); ps.setString(++i, job.getJobId()); + if(errorSummary != null) + { + ps.setString(++i, errorSummary.getMessage()); + ps.setObject(++i, errorSummary.getType().name(), Types.OTHER); + ps.setBoolean(++i, errorSummary.isHasDetail()); + } }); } diff --git a/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java b/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java index a35dc3d..ea46eda 100644 --- a/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java +++ b/src/test/java/it/inaf/oats/vospace/TransferControllerTest.java @@ -2,6 +2,7 @@ package it.inaf.oats.vospace; import it.inaf.ia2.aa.data.User; import static it.inaf.oats.vospace.VOSpaceXmlTestUtil.loadDocument; +import it.inaf.oats.vospace.datamodel.NodeProperties; import it.inaf.oats.vospace.persistence.JobDAO; import it.inaf.oats.vospace.persistence.LocationDAO; import it.inaf.oats.vospace.persistence.NodeDAO; @@ -155,6 +156,7 @@ public class TransferControllerTest { when(nodeDao.listNode(eq(path))).thenReturn(Optional.of(node)); String redirect = mockMvc.perform(post("/transfers?PHASE=RUN") + .header("Authorization", "Bearer user1_token") .content(requestBody) .contentType(MediaType.APPLICATION_XML) .accept(MediaType.APPLICATION_XML)) @@ -224,6 +226,17 @@ public class TransferControllerTest { property.setUri("ivo://ivoa.net/vospace/core#publicread"); property.setValue("true"); node.getProperties().add(property); + + Property ownerProp = new Property(); + ownerProp.setUri(NodeProperties.CREATOR_URI); + ownerProp.setValue("user1"); + node.getProperties().add(ownerProp); + + Property groupProp = new Property(); + groupProp.setUri(NodeProperties.GROUP_WRITE_URI); + groupProp.setValue("group1"); + node.getProperties().add(groupProp); + return node; } diff --git a/src/test/java/it/inaf/oats/vospace/UriServiceTest.java b/src/test/java/it/inaf/oats/vospace/UriServiceTest.java index 00a1166..84b865d 100644 --- a/src/test/java/it/inaf/oats/vospace/UriServiceTest.java +++ b/src/test/java/it/inaf/oats/vospace/UriServiceTest.java @@ -93,11 +93,21 @@ public class UriServiceTest { public void testPrivateUrl() { Node node = new DataNode(); + Property creator = new Property(); + creator.setUri(NodeProperties.CREATOR_URI); + creator.setValue("user1"); + node.getProperties().add(creator); + + Property readgroup = new Property(); + readgroup.setUri(NodeProperties.GROUP_READ_URI); + readgroup.setValue("group1"); + node.getProperties().add(readgroup); when(nodeDAO.listNode(eq("/mydata1"))).thenReturn(Optional.of(node)); User user = mock(User.class); when(user.getAccessToken()).thenReturn("<token>"); + when(user.getName()).thenReturn("user1"); when(servletRequest.getUserPrincipal()).thenReturn(user); @@ -108,7 +118,8 @@ public class UriServiceTest { }), any())).thenReturn("<new-token>"); JobSummary job = getJob(); - uriService.setTransferJobResult(job, uriService.getTransfer(job)); + Transfer tr = uriService.getTransfer(job); + uriService.setTransferJobResult(job, tr); assertEquals("http://file-service/mydata1?jobId=job-id&token=<new-token>", job.getResults().get(0).getHref()); } @@ -133,6 +144,7 @@ public class UriServiceTest { Transfer transfer = new Transfer(); transfer.setTarget("vos://example.com!vospace/mydata1"); + transfer.setDirection(JobService.JobType.pullFromVoSpace.toString()); JobSummary job = new JobSummary(); job.setJobId("job-id"); -- GitLab