Loading vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java +5 −4 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import it.inaf.ia2.aa.data.User; import it.inaf.ia2.vospace.ui.VOSpaceUiApplication; import it.inaf.ia2.vospace.ui.data.Job; import it.inaf.ia2.vospace.ui.exception.VOSpaceException; import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; Loading Loading @@ -72,7 +73,7 @@ public class VOSpaceClient { public Node getNode(String path) { HttpRequest request = getRequest("/nodes" + path) HttpRequest request = getRequest("/nodes" + urlEncodePath(path)) .header("Accept", useJson ? "application/json" : "text/xml") .build(); Loading Loading @@ -105,7 +106,7 @@ public class VOSpaceClient { String path = node.getUri().substring(("vos://" + authority).length()); HttpRequest request = getRequest("/nodes" + path) HttpRequest request = getRequest("/nodes" + urlEncodePath(path)) .header("Accept", useJson ? "application/json" : "text/xml") .header("Content-Type", useJson ? "application/json" : "text/xml") .PUT(HttpRequest.BodyPublishers.ofString(marshal(node))) Loading @@ -116,7 +117,7 @@ public class VOSpaceClient { public void deleteNode(String path) { HttpRequest request = getRequest("/nodes" + path) HttpRequest request = getRequest("/nodes" + urlEncodePath(path)) .header("Accept", useJson ? "application/json" : "text/xml") .header("Content-Type", useJson ? "application/json" : "text/xml") .DELETE() Loading vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java +13 −15 Original line number Diff line number Diff line Loading @@ -4,12 +4,15 @@ import it.inaf.ia2.aa.data.User; import it.inaf.ia2.vospace.ui.client.VOSpaceClient; import it.inaf.ia2.vospace.ui.data.ListNodeData; import it.inaf.ia2.vospace.ui.service.NodesService; import it.inaf.oats.vospace.datamodel.NodeUtils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; Loading @@ -25,6 +28,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class NodesController extends BaseController { private static final Logger LOG = LoggerFactory.getLogger(NodesController.class); @Value("${vospace-authority}") private String authority; Loading @@ -41,6 +46,7 @@ public class NodesController extends BaseController { public ResponseEntity<ListNodeData> listNodes(User principal) throws Exception { String path = getPath("/nodes/"); LOG.debug("listNodes called for path {}", path); return ResponseEntity.ok(nodesService.generateNodesHtml(path, principal)); } Loading @@ -48,6 +54,7 @@ public class NodesController extends BaseController { @DeleteMapping(value = {"/nodes", "/nodes/**"}) public void deleteNode() { String path = getPath("/nodes/"); LOG.debug("deleteNode called for path {}", path); client.deleteNode(path); } Loading @@ -55,6 +62,7 @@ public class NodesController extends BaseController { public ResponseEntity<?> directDownload() { String path = getPath("/download/"); LOG.debug("directDownload called for path {}", path); Transfer transfer = new Transfer(); transfer.setDirection("pullFromVoSpace"); Loading @@ -79,6 +87,8 @@ public class NodesController extends BaseController { } String name = getRequiredParam(params, "name"); LOG.debug("newFolder called for path {}/{}", parentPath, name); ContainerNode node = new ContainerNode(); node.setUri("vos://" + authority + parentPath + "/" + name); Loading @@ -90,20 +100,8 @@ public class NodesController extends BaseController { client.createNode(node); } /** * Slash is a special character in defining REST endpoints and trying to * define a PathVariable containing slashes doesn't work, so the endpoint * has been defined using "/nodes/**" instead of "/nodes/{path}" and the * path is extracted manually parsing the request URL. */ protected String getPath(String prefix) { String requestURL = servletRequest.getRequestURL().toString(); String[] split = requestURL.split(prefix); String path = "/"; if (split.length == 2) { path += split[1]; } return path; return NodeUtils.getPathFromRequestURLString(requestURL, prefix); } } vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodesService.java +4 −3 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import it.inaf.ia2.aa.data.User; import it.inaf.ia2.vospace.ui.client.VOSpaceClient; import it.inaf.ia2.vospace.ui.data.ListNodeData; import it.inaf.oats.vospace.datamodel.NodeUtils; import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath; import java.io.IOException; import java.io.StringWriter; import java.io.UncheckedIOException; Loading Loading @@ -65,7 +66,7 @@ public class NodesService { } String html = "<tr>"; html += "<td><input type=\"checkbox\" data-node=\"" + nodeInfo.getPath() + "\" "; html += "<td><input type=\"checkbox\" data-node=\"" + nodeInfo.getPath().replace("\"", "\\\"") + "\" "; if (nodeInfo.isAsyncTrans()) { html += "class=\"async\""; } Loading Loading @@ -108,9 +109,9 @@ public class NodesService { private String getLink(NodeInfo nodeInfo, User user) { if (isDownloadable(nodeInfo, user)) { if (nodeInfo.isFolder()) { return "<a href=\"#/nodes" + nodeInfo.getPath() + "\">" + nodeInfo.getName() + "</a>"; return "<a href=\"#/nodes" + urlEncodePath(nodeInfo.getPath()) + "\">" + nodeInfo.getName() + "</a>"; } else { return "<a href=\"download" + nodeInfo.getPath() + "\" target=\"blank_\">" + nodeInfo.getName() + "</a>"; return "<a href=\"download" + urlEncodePath(nodeInfo.getPath()) + "\" target=\"blank_\">" + nodeInfo.getName() + "</a>"; } } return nodeInfo.getName(); Loading vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/client/VOSpaceClientTest.java +14 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,20 @@ public class VOSpaceClientTest { assertEquals(newNode.getUri(), responseNode.getUri()); } @Test public void testCreateNodeBadName() { ContainerNode newNode = new ContainerNode(); newNode.setUri("vos://ia2.inaf.it!vospace/mynode/File with spaces.and.dots.pdf"); ReflectionTestUtils.setField(voSpaceClient, "useJson", false); CompletableFuture response = getMockedStreamResponseFuture(200, getResourceFileContent("node-response.xml")); when(mockedHttpClient.sendAsync(any(), any())).thenReturn(response); voSpaceClient.createNode(newNode); } protected static String getResourceFileContent(String fileName) { try ( InputStream in = VOSpaceClientTest.class.getClassLoader().getResourceAsStream(fileName)) { return new String(in.readAllBytes(), StandardCharsets.UTF_8); Loading vospace-ui-frontend/src/api/server/index.js +6 −2 Original line number Diff line number Diff line Loading @@ -41,9 +41,13 @@ function getErrorMessage(error) { } } function escapePath(path) { return path.split('/').map(p => encodeURIComponent(p)).join('/'); } export default { getNode(path) { let url = BASE_API_URL + 'nodes/' + path; let url = BASE_API_URL + 'nodes/' + escapePath(path); return apiRequest({ method: 'GET', url: url, Loading Loading @@ -127,7 +131,7 @@ export default { }) }, deleteNode(path) { let url = BASE_API_URL + 'nodes' + path; let url = BASE_API_URL + 'nodes' + escapePath(path); return apiRequest({ method: 'DELETE', url: url, Loading Loading
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/client/VOSpaceClient.java +5 −4 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import it.inaf.ia2.aa.data.User; import it.inaf.ia2.vospace.ui.VOSpaceUiApplication; import it.inaf.ia2.vospace.ui.data.Job; import it.inaf.ia2.vospace.ui.exception.VOSpaceException; import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; Loading Loading @@ -72,7 +73,7 @@ public class VOSpaceClient { public Node getNode(String path) { HttpRequest request = getRequest("/nodes" + path) HttpRequest request = getRequest("/nodes" + urlEncodePath(path)) .header("Accept", useJson ? "application/json" : "text/xml") .build(); Loading Loading @@ -105,7 +106,7 @@ public class VOSpaceClient { String path = node.getUri().substring(("vos://" + authority).length()); HttpRequest request = getRequest("/nodes" + path) HttpRequest request = getRequest("/nodes" + urlEncodePath(path)) .header("Accept", useJson ? "application/json" : "text/xml") .header("Content-Type", useJson ? "application/json" : "text/xml") .PUT(HttpRequest.BodyPublishers.ofString(marshal(node))) Loading @@ -116,7 +117,7 @@ public class VOSpaceClient { public void deleteNode(String path) { HttpRequest request = getRequest("/nodes" + path) HttpRequest request = getRequest("/nodes" + urlEncodePath(path)) .header("Accept", useJson ? "application/json" : "text/xml") .header("Content-Type", useJson ? "application/json" : "text/xml") .DELETE() Loading
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/controller/NodesController.java +13 −15 Original line number Diff line number Diff line Loading @@ -4,12 +4,15 @@ import it.inaf.ia2.aa.data.User; import it.inaf.ia2.vospace.ui.client.VOSpaceClient; import it.inaf.ia2.vospace.ui.data.ListNodeData; import it.inaf.ia2.vospace.ui.service.NodesService; import it.inaf.oats.vospace.datamodel.NodeUtils; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; Loading @@ -25,6 +28,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class NodesController extends BaseController { private static final Logger LOG = LoggerFactory.getLogger(NodesController.class); @Value("${vospace-authority}") private String authority; Loading @@ -41,6 +46,7 @@ public class NodesController extends BaseController { public ResponseEntity<ListNodeData> listNodes(User principal) throws Exception { String path = getPath("/nodes/"); LOG.debug("listNodes called for path {}", path); return ResponseEntity.ok(nodesService.generateNodesHtml(path, principal)); } Loading @@ -48,6 +54,7 @@ public class NodesController extends BaseController { @DeleteMapping(value = {"/nodes", "/nodes/**"}) public void deleteNode() { String path = getPath("/nodes/"); LOG.debug("deleteNode called for path {}", path); client.deleteNode(path); } Loading @@ -55,6 +62,7 @@ public class NodesController extends BaseController { public ResponseEntity<?> directDownload() { String path = getPath("/download/"); LOG.debug("directDownload called for path {}", path); Transfer transfer = new Transfer(); transfer.setDirection("pullFromVoSpace"); Loading @@ -79,6 +87,8 @@ public class NodesController extends BaseController { } String name = getRequiredParam(params, "name"); LOG.debug("newFolder called for path {}/{}", parentPath, name); ContainerNode node = new ContainerNode(); node.setUri("vos://" + authority + parentPath + "/" + name); Loading @@ -90,20 +100,8 @@ public class NodesController extends BaseController { client.createNode(node); } /** * Slash is a special character in defining REST endpoints and trying to * define a PathVariable containing slashes doesn't work, so the endpoint * has been defined using "/nodes/**" instead of "/nodes/{path}" and the * path is extracted manually parsing the request URL. */ protected String getPath(String prefix) { String requestURL = servletRequest.getRequestURL().toString(); String[] split = requestURL.split(prefix); String path = "/"; if (split.length == 2) { path += split[1]; } return path; return NodeUtils.getPathFromRequestURLString(requestURL, prefix); } }
vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodesService.java +4 −3 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import it.inaf.ia2.aa.data.User; import it.inaf.ia2.vospace.ui.client.VOSpaceClient; import it.inaf.ia2.vospace.ui.data.ListNodeData; import it.inaf.oats.vospace.datamodel.NodeUtils; import static it.inaf.oats.vospace.datamodel.NodeUtils.urlEncodePath; import java.io.IOException; import java.io.StringWriter; import java.io.UncheckedIOException; Loading Loading @@ -65,7 +66,7 @@ public class NodesService { } String html = "<tr>"; html += "<td><input type=\"checkbox\" data-node=\"" + nodeInfo.getPath() + "\" "; html += "<td><input type=\"checkbox\" data-node=\"" + nodeInfo.getPath().replace("\"", "\\\"") + "\" "; if (nodeInfo.isAsyncTrans()) { html += "class=\"async\""; } Loading Loading @@ -108,9 +109,9 @@ public class NodesService { private String getLink(NodeInfo nodeInfo, User user) { if (isDownloadable(nodeInfo, user)) { if (nodeInfo.isFolder()) { return "<a href=\"#/nodes" + nodeInfo.getPath() + "\">" + nodeInfo.getName() + "</a>"; return "<a href=\"#/nodes" + urlEncodePath(nodeInfo.getPath()) + "\">" + nodeInfo.getName() + "</a>"; } else { return "<a href=\"download" + nodeInfo.getPath() + "\" target=\"blank_\">" + nodeInfo.getName() + "</a>"; return "<a href=\"download" + urlEncodePath(nodeInfo.getPath()) + "\" target=\"blank_\">" + nodeInfo.getName() + "</a>"; } } return nodeInfo.getName(); Loading
vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/client/VOSpaceClientTest.java +14 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,20 @@ public class VOSpaceClientTest { assertEquals(newNode.getUri(), responseNode.getUri()); } @Test public void testCreateNodeBadName() { ContainerNode newNode = new ContainerNode(); newNode.setUri("vos://ia2.inaf.it!vospace/mynode/File with spaces.and.dots.pdf"); ReflectionTestUtils.setField(voSpaceClient, "useJson", false); CompletableFuture response = getMockedStreamResponseFuture(200, getResourceFileContent("node-response.xml")); when(mockedHttpClient.sendAsync(any(), any())).thenReturn(response); voSpaceClient.createNode(newNode); } protected static String getResourceFileContent(String fileName) { try ( InputStream in = VOSpaceClientTest.class.getClassLoader().getResourceAsStream(fileName)) { return new String(in.readAllBytes(), StandardCharsets.UTF_8); Loading
vospace-ui-frontend/src/api/server/index.js +6 −2 Original line number Diff line number Diff line Loading @@ -41,9 +41,13 @@ function getErrorMessage(error) { } } function escapePath(path) { return path.split('/').map(p => encodeURIComponent(p)).join('/'); } export default { getNode(path) { let url = BASE_API_URL + 'nodes/' + path; let url = BASE_API_URL + 'nodes/' + escapePath(path); return apiRequest({ method: 'GET', url: url, Loading Loading @@ -127,7 +131,7 @@ export default { }) }, deleteNode(path) { let url = BASE_API_URL + 'nodes' + path; let url = BASE_API_URL + 'nodes' + escapePath(path); return apiRequest({ method: 'DELETE', url: url, Loading