Newer
Older
import it.inaf.ia2.aa.data.User;
import it.inaf.oats.vospace.persistence.JobDAO;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.uws.v1.ExecutionPhase;
import net.ivoa.xml.uws.v1.JobSummary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
Sonia Zorba
committed
import java.util.Arrays;
import java.util.HashSet;
import net.ivoa.xml.uws.v1.ErrorSummary;
Sonia Zorba
committed
import net.ivoa.xml.vospace.v2.Protocol;
@RestController
public class TransferController {
@Autowired
private JobDAO jobDAO;
@Autowired
private HttpServletRequest request;
@Autowired
private JobService jobService;
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 ResponseEntity<?> postAsyncTransfer(@RequestBody Transfer transfer,
@RequestParam(value = "PHASE", required = false) Optional<String> phase, User principal) {
JobSummary jobSummary = newJobSummary(transfer, principal);
if (phase.isPresent()) {
jobService.setJobPhase(jobSummary, phase.get());
return getJobRedirect(jobSummary.getJobId());
}
@PostMapping(value = "/synctrans",
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 ResponseEntity<?> postSyncTransfer(@RequestBody Transfer transfer, User principal) {
JobSummary jobSummary = newJobSummary(transfer, principal);
jobService.createSyncJobResult(jobSummary);
HttpHeaders headers = new HttpHeaders();
headers.set("Location", request.getContextPath() + "/transfers/" + jobSummary.getJobId() + "/results/transferDetails");
return new ResponseEntity<>(headers, HttpStatus.SEE_OTHER);
Sonia Zorba
committed
@GetMapping("/synctrans")
public ResponseEntity<?> syncTransferUrlParamsMode(@RequestParam("TARGET") String target,
@RequestParam("DIRECTION") String direction, @RequestParam("PROTOCOL") String protocolUris, User principal) {
Transfer transfer = new Transfer();
transfer.setTarget(target);
transfer.setDirection(direction);
// CADC client sends multiple protocol parameters and Spring join them using a comma separator.
// Some values seems to be duplicated, so a set is used to avoid the duplication
for (String protocolUri : new HashSet<>(Arrays.asList(protocolUris.split(",")))) {
Protocol protocol = new Protocol();
protocol.setUri(protocolUri);
transfer.getProtocols().add(protocol);
}
JobSummary jobSummary = newJobSummary(transfer, principal);
jobService.createSyncJobResult(jobSummary);
if (jobSummary.getErrorSummary() != null) {
// TODO: decide how to hanlde HTTP error codes
// If an error occurs with the synchronous convenience mode where the preferred endpoint
// is immediately returned as a redirect, the error information is returned directly in
// the response body with the associated HTTP status code.
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(jobSummary.getErrorSummary().getMessage());
}
Sonia Zorba
committed
// Behaves as if REQUEST=redirect was set, for compatibility with CADC client
String endpoint = transfer.getProtocols().get(0).getEndpoint();
HttpHeaders headers = new HttpHeaders();
headers.set("Location", endpoint);
return new ResponseEntity<>(headers, HttpStatus.SEE_OTHER);
}
@GetMapping(value = "/transfers/{jobId}", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<JobSummary> getJob(@PathVariable("jobId") String jobId) {
return jobDAO.getJob(jobId).map(j -> ResponseEntity.ok(j)).orElse(ResponseEntity.notFound().build());
}
@GetMapping(value = "/transfers/{jobId}/error", produces = {MediaType.TEXT_PLAIN_VALUE})
public ResponseEntity<String> getJobError(@PathVariable("jobId") String jobId) {
return jobDAO.getJob(jobId).map(j -> {
if (j.getPhase().equals(ExecutionPhase.ERROR)) {
ErrorSummary e = j.getErrorSummary();
if (e.isHasDetail()) {
return ResponseEntity.ok(e.getDetailMessage());
} else {
return ResponseEntity.ok("No error details available");
}
} else {
return ResponseEntity.ok("Job is not in ERROR phase");
}
}).orElse(ResponseEntity.notFound().build());
}
@PostMapping(value = "/transfers/{jobId}/phase")
public ResponseEntity<?> setJobPhase(@PathVariable("jobId") String jobId, @RequestParam("PHASE") String phase, User principal) {
return jobDAO.getJob(jobId).map(job -> {
if (!job.getOwnerId().equals(principal.getName())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
jobService.setJobPhase(job, phase);
return getJobRedirect(job.getJobId());
}).orElse(ResponseEntity.notFound().build());
}
@GetMapping(value = "/transfers/{jobId}/results/transferDetails", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<?> getTransferDetails(@PathVariable("jobId") String jobId, User principal) {
return jobDAO.getJob(jobId).map(job -> {
if (!job.getOwnerId().equals(principal.getName())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
// TODO: check type
return ResponseEntity.ok((Transfer) (job.getJobInfo().getAny().get(0)));
}).orElse(ResponseEntity.notFound().build());
}
@GetMapping(value = "/transfers/{jobId}/phase", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<?> getTransferJobPhase(@PathVariable("jobId") String jobId, User principal) {
// TODO: error handling
return ResponseEntity.ok(jobDAO.getJob(jobId).get().getPhase().toString());
}
@GetMapping(value = "/transfers", produces = {MediaType.APPLICATION_XML_VALUE, MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<?> getTransfers(
@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 = "LAST", required = false) Optional<Integer> last,
@RequestParam(value = "direction", required = false) Optional<List<JobService.JobDirection>> direction,
Nicola Fulvio Calabria
committed
User principal) {
if (last.isPresent()) {
if (last.get() <= 0) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
Nicola Fulvio Calabria
committed
}
}
String userId = principal.getName();
List<ExecutionPhase> phaseList;
if (phase.isPresent()) {
phaseList = phase.get();
} else {
phaseList = List.of();
}
List<JobService.JobDirection> directionList;
if (direction.isPresent()) {
directionList = direction.get();
} else {
directionList = List.of();
Nicola Fulvio Calabria
committed
}
Jobs jobs = jobDAO.getJobs(userId, phaseList, directionList, after, last);
return ResponseEntity.ok(jobs);
}
private JobSummary newJobSummary(Transfer transfer, User principal) {
String jobId = UUID.randomUUID().toString().replace("-", "");
Sonia Zorba
committed
JobSummary jobSummary = new JobSummary();
jobSummary.setJobId(jobId);
jobSummary.setOwnerId(principal.getName());
jobSummary.setPhase(ExecutionPhase.PENDING);
JobSummary.JobInfo jobInfo = new JobSummary.JobInfo();
jobInfo.getAny().add(transfer);
jobSummary.setJobInfo(jobInfo);
return jobSummary;
}
private ResponseEntity<?> getJobRedirect(String jobId) {
HttpHeaders headers = new HttpHeaders();
headers.set("Location", request.getContextPath() + "/transfers/" + jobId);
return new ResponseEntity<>(headers, HttpStatus.SEE_OTHER);