/* * 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 com.fasterxml.jackson.databind.ObjectMapper; import it.inaf.ia2.aa.data.User; import it.inaf.oats.vospace.datamodel.NodeUtils; import it.inaf.oats.vospace.datamodel.Views; import it.inaf.oats.vospace.exception.InvalidArgumentException; import it.inaf.oats.vospace.exception.NodeNotFoundException; import it.inaf.oats.vospace.parent.exchange.ArchiveEntryDescriptor; import it.inaf.oats.vospace.persistence.NodeDAO; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import net.ivoa.xml.vospace.v2.ContainerNode; import net.ivoa.xml.vospace.v2.Node; import net.ivoa.xml.vospace.v2.Transfer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class FileServiceClient { private static final ObjectMapper MAPPER = new ObjectMapper(); @Value("${vospace-authority}") private String authority; @Value("${file-service-url}") private String fileServiceUrl; @Autowired private RestTemplate restTemplate; @Autowired private HttpServletRequest request; @Autowired private LinkService linkService; @Autowired private NodeDAO nodeDAO; public String startArchiveJob(Transfer transfer, String jobId) { String target = NodeUtils.getVosPath(transfer.getTarget()); String viewUri = transfer.getView().getUri(); // Generate list of paths using view include parameters List 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); } // Generate descriptors // Expand container nodes into direct children list List expandedVosPaths = new ArrayList<>(); for (String vosPath : vosPaths) { Node node = nodeDAO.listNode(vosPath) .orElseThrow(() -> { throw new NodeNotFoundException(vosPath); }); if (node instanceof ContainerNode) { List nodes = ((ContainerNode) node).getNodes(); if (nodes.isEmpty()) { expandedVosPaths.add(NodeUtils.getVosPath(node)); } else { expandedVosPaths.addAll(nodes .stream().map(n -> NodeUtils.getVosPath(n)) .collect(Collectors.toList())); } } else { expandedVosPaths.add(vosPath); } } // follow links to links in vosPaths List entryDescriptors = linkService.followLinksForArchiveService(expandedVosPaths); ArchiveRequest archiveRequest = new ArchiveRequest(); archiveRequest.setJobId(jobId); archiveRequest.setEntryDescriptors(entryDescriptors); archiveRequest.setType(archiveTypeFromViewUri(transfer.getView().getUri())); String url = fileServiceUrl + "/archive"; String token = ((User) request.getUserPrincipal()).getAccessToken(); return restTemplate.execute(url, HttpMethod.POST, req -> { HttpHeaders headers = req.getHeaders(); if (token != null) { headers.setBearerAuth(token); } headers.setContentType(MediaType.APPLICATION_JSON); try (OutputStream os = req.getBody()) { MAPPER.writeValue(os, archiveRequest); } }, res -> { return res.getHeaders().getLocation().toString(); }, new Object[]{}); } public void startFileCopyJob(String sourceVosPath, String destiantionVosPath, String jobId, User user) { CopyRequest copyRequest = new CopyRequest(); copyRequest.setJobId(jobId); copyRequest.setSourceRootVosPath(sourceVosPath); copyRequest.setDestinationRootVosPath(destiantionVosPath); String url = fileServiceUrl + "/copy"; String token = user.getAccessToken(); restTemplate.execute(url, HttpMethod.POST, req -> { HttpHeaders headers = req.getHeaders(); if (token != null) { headers.setBearerAuth(token); } headers.setContentType(MediaType.APPLICATION_JSON); try (OutputStream os = req.getBody()) { MAPPER.writeValue(os, copyRequest); } }, res -> { return null; }, new Object[]{}); } public static class CopyRequest { private String jobId; private String sourceRootVosPath; private String destinationRootVosPath; public String getJobId() { return jobId; } public void setJobId(String jobId) { this.jobId = jobId; } public String getSourceRootVosPath() { return sourceRootVosPath; } public void setSourceRootVosPath(String sourceRootVosPath) { this.sourceRootVosPath = sourceRootVosPath; } public String getDestinationRootVosPath() { return destinationRootVosPath; } public void setDestinationRootVosPath(String destinationRootVosPath) { this.destinationRootVosPath = destinationRootVosPath; } } public static class ArchiveRequest { private String type; private String jobId; private List entryDescriptors; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getJobId() { return jobId; } public void setJobId(String jobId) { this.jobId = jobId; } public List getEntryDescriptors() { return entryDescriptors; } public void setEntryDescriptors(List entryDescriptors) { this.entryDescriptors = entryDescriptors; } } private static String archiveTypeFromViewUri(String viewUri) { switch (viewUri) { case Views.TAR_VIEW_URI: return "TAR"; case Views.ZIP_VIEW_URI: return "ZIP"; default: throw new IllegalArgumentException("Archive type not defined for " + viewUri); } } }