Loading vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java +1 −1 Original line number Diff line number Diff line Loading @@ -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") Loading vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/BaseController.java 0 → 100644 +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); } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java +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; Loading @@ -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; Loading Loading @@ -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("/")) { Loading @@ -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); } /** Loading vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java 0 → 100644 +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(); } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/data/UploadFilesData.java 0 → 100644 +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
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java +1 −1 Original line number Diff line number Diff line Loading @@ -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") Loading
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/BaseController.java 0 → 100644 +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); } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java +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; Loading @@ -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; Loading Loading @@ -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("/")) { Loading @@ -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); } /** Loading
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/UploadController.java 0 → 100644 +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(); } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/data/UploadFilesData.java 0 → 100644 +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; } }