Commit 17eb0e40 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

Implemented file upload

parent 7a33ad06
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ public class VOSpaceClient {
        return call(request, BodyHandlers.ofInputStream(), 200, res -> unmarshal(res, JobSummary.class));
    }

    public List<Protocol> getDownloadEndpoints(Transfer transfer) {
    public List<Protocol> getFileServiceEndpoints(Transfer transfer) {

        HttpRequest request = getRequest("/synctrans")
                .header("Accept", useJson ? "application/json" : "text/xml")
+31 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui.controller;

import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.vospace.ui.exception.BadRequestException;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;

public class BaseController {

    @Autowired
    private HttpSession session;

    protected User getUser() {
        return (User) session.getAttribute("user_data");
    }

    protected String getRequiredParam(Map<String, Object> params, String key) {
        if (!params.containsKey(key)) {
            throw new BadRequestException("Missing mandatory parameter " + key);
        }
        return (String) params.get(key);
    }

    protected <T> T getRequiredParam(Map<String, Object> params, String key, Class<T> type) {
        if (!params.containsKey(key)) {
            throw new BadRequestException("Missing mandatory parameter " + key);
        }
        return (T) params.get(key);
    }
}
+9 −11
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui.controller;

import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
import it.inaf.ia2.vospace.ui.exception.BadRequestException;
import it.inaf.ia2.vospace.ui.service.NodesService;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import net.ivoa.xml.vospace.v2.ContainerNode;
import net.ivoa.xml.vospace.v2.Property;
import net.ivoa.xml.vospace.v2.Protocol;
import net.ivoa.xml.vospace.v2.Transfer;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class NodesController {
public class NodesController extends BaseController {

    @Value("${vospace-authority}")
    private String authority;
@@ -63,14 +63,14 @@ public class NodesController {
        protocol.setUri("ivo://ivoa.net/vospace/core#httpget");
        transfer.getProtocols().add(protocol);

        String url = client.getDownloadEndpoints(transfer).get(0).getEndpoint();
        String url = client.getFileServiceEndpoints(transfer).get(0).getEndpoint();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Location", url);
        return new ResponseEntity<>(headers, HttpStatus.SEE_OTHER);
    }

    @PostMapping(value = "/folder")
    public void newFolder(@RequestBody Map<String, String> params) {
    public void newFolder(@RequestBody Map<String, Object> params) {

        String parentPath = getRequiredParam(params, "parentPath");
        if (!parentPath.startsWith("/")) {
@@ -81,14 +81,12 @@ public class NodesController {
        ContainerNode node = new ContainerNode();
        node.setUri("vos://" + authority + parentPath + "/" + name);
        
        client.createNode(node);
    }
        Property creator = new Property();
        creator.setUri("ivo://ivoa.net/vospace/core#creator");
        creator.setValue(getUser().getName());
        node.getProperties().add(creator);

    private String getRequiredParam(Map<String, String> params, String key) {
        if (!params.containsKey(key)) {
            throw new BadRequestException("Missing mandatory parameter " + key);
        }
        return params.get(key);
        client.createNode(node);
    }

    /**
+96 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui.controller;

import it.inaf.ia2.vospace.ui.client.VOSpaceClient;
import it.inaf.ia2.vospace.ui.data.UploadFilesData;
import it.inaf.ia2.vospace.ui.exception.PermissionDeniedException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import javax.validation.Valid;
import net.ivoa.xml.vospace.v2.DataNode;
import net.ivoa.xml.vospace.v2.Property;
import net.ivoa.xml.vospace.v2.Protocol;
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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UploadController extends BaseController {

    @Value("${vospace-authority}")
    private String authority;

    @Autowired
    private VOSpaceClient client;

    @PostMapping(value = "/preupload", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<List<String>> prepareForUpload(@RequestBody @Valid UploadFilesData data) {

        if (getUser() == null) {
            throw new PermissionDeniedException("File upload not allowed to anonymous users");
        }

        CompletableFuture<String>[] calls
                = data.getFiles().stream().map(fileName -> prepareForDownload(getParentPath(data), fileName))
                        .toArray(CompletableFuture[]::new);

        List<String> uploadUrls = CompletableFuture.allOf(calls)
                .thenApplyAsync(ignore -> {
                    return Arrays.stream(calls).map(c -> c.join()).collect(Collectors.toList());
                }).join();

        return ResponseEntity.ok(uploadUrls);
    }

    private String getParentPath(UploadFilesData data) {
        String parentPath = data.getParentPath();
        if (!parentPath.startsWith("/")) {
            parentPath = "/" + parentPath;
        }
        return parentPath;
    }

    public CompletableFuture<String> prepareForDownload(String parentPath, String fileName) {

        return CompletableFuture.supplyAsync(() -> {
            String nodeUri = "vos://" + authority + parentPath + "/" + fileName;

            createDataNode(nodeUri, getUser().getName());

            return obtainUploadUrl(nodeUri);
        }, Runnable::run); // Passing current thread Executor to CompletableFuture to avoid "No thread-bound request found" exception
    }

    private void createDataNode(String nodeUri, String userId) {

        DataNode node = new DataNode();
        node.setUri(nodeUri);

        Property creator = new Property();
        creator.setUri("ivo://ivoa.net/vospace/core#creator");
        creator.setValue(userId);

        node.getProperties().add(creator);

        client.createNode(node);
    }

    private String obtainUploadUrl(String uri) {

        Transfer transfer = new Transfer();
        transfer.setDirection("pushToVoSpace");
        transfer.setTarget(uri);

        Protocol protocol = new Protocol();
        protocol.setUri("ivo://ivoa.net/vospace/core#httpget");
        transfer.getProtocols().add(protocol);

        return client.getFileServiceEndpoints(transfer).get(0).getEndpoint();
    }
}
+28 −0
Original line number Diff line number Diff line
package it.inaf.ia2.vospace.ui.data;

import java.util.List;
import javax.validation.constraints.NotNull;

public class UploadFilesData {

    @NotNull
    private String parentPath;
    @NotNull
    private List<String> files;

    public String getParentPath() {
        return parentPath;
    }

    public void setParentPath(String parentPath) {
        this.parentPath = parentPath;
    }

    public List<String> getFiles() {
        return files;
    }

    public void setFiles(List<String> files) {
        this.files = files;
    }
}
Loading