From 6ef14f6de8743cc65004d6c0103c7a655b81da2c Mon Sep 17 00:00:00 2001 From: Sonia Zorba <sonia.zorba@inaf.it> Date: Tue, 9 Feb 2021 17:46:31 +0100 Subject: [PATCH] Fixed file size bug; called keepalive endpoint for refreshing tokens; improved icon for busy nodes --- .../inaf/ia2/vospace/ui/service/NodeInfo.java | 13 ++-- .../ia2/vospace/ui/service/NodesService.java | 29 ++++---- .../ia2/vospace/ui/service/NodeInfoTest.java | 67 +++++++++++++++++++ vospace-ui-frontend/src/App.vue | 17 +++++ vospace-ui-frontend/src/api/mock/index.js | 3 + vospace-ui-frontend/src/api/server/index.js | 11 +++ vospace-ui-frontend/src/assets/css/fonts.css | 4 -- 7 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java index 8de4ace..77e8614 100644 --- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java +++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodeInfo.java @@ -128,17 +128,14 @@ public class NodeInfo { } /** - * Credits: https://stackoverflow.com/a/16576773/771431 + * Credits: https://stackoverflow.com/a/24805871/771431 */ private String getHumanReadableSize(long bytes) { - int u = 0; - for (; bytes > 1024 * 1024; bytes >>= 10) { - u++; + if (bytes < 1024) { + return bytes + " B"; } - if (bytes > 1024) { - u++; - } - return String.format("%.1f %cB", bytes / 1024f, " kMGTPE".charAt(u)); + int z = (63 - Long.numberOfLeadingZeros(bytes)) / 10; + return String.format("%.1f %sB", (double) bytes / (1L << (z * 10)), " KMGTPE".charAt(z)); } public boolean isFolder() { diff --git a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodesService.java b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodesService.java index 018ccc2..d8a5b4e 100644 --- a/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodesService.java +++ b/vospace-ui-backend/src/main/java/it/inaf/ia2/vospace/ui/service/NodesService.java @@ -3,7 +3,6 @@ package it.inaf.ia2.vospace.ui.service; 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.NodeProperties; import it.inaf.oats.vospace.datamodel.NodeUtils; import java.io.IOException; import java.io.StringWriter; @@ -85,20 +84,24 @@ public class NodesService { } private String getIcon(NodeInfo nodeInfo) { - String html = "<span class=\"icon "; - if (nodeInfo.isFile() && nodeInfo.isBusy()) { - html += "gear"; + String html = ""; + if (nodeInfo.isBusy()) { + html += "<span class=\"node-busy\"><span role=\"status\" class=\"spinner-border\"><span class=\"sr-only\">Loading...</span></span>"; + } + html += "<span class=\"icon "; + if (nodeInfo.isFolder()) { + html += "folder"; } else { - if (nodeInfo.isFolder()) { - html += "folder"; - } else { - html += "file"; - } - if (nodeInfo.isAsyncTrans()) { - html += "-x"; - } + html += "file"; + } + if (nodeInfo.isAsyncTrans()) { + html += "-x"; + } + html += "-icon\"></span>"; + if (nodeInfo.isBusy()) { + html += "</span>"; } - html += "-icon\"></span> "; + html += " "; return html; } diff --git a/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java new file mode 100644 index 0000000..f113953 --- /dev/null +++ b/vospace-ui-backend/src/test/java/it/inaf/ia2/vospace/ui/service/NodeInfoTest.java @@ -0,0 +1,67 @@ +package it.inaf.ia2.vospace.ui.service; + +import net.ivoa.xml.vospace.v2.DataNode; +import net.ivoa.xml.vospace.v2.Node; +import net.ivoa.xml.vospace.v2.Property; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +public class NodeInfoTest { + + private static final String AUTHORITY = "example.com!vospace"; + + @Test + public void testZero() { + testNodeLength(0, "0 B"); + } + + @Test + public void testSizeBytes() { + testNodeLength(5, "5 B"); + } + + @Test + public void testSizeKilo() { + testNodeLength(1024, "1.0 KB"); + } + + @Test + public void testSizeMega() { + testNodeLength(149639144, "142.7 MB"); + } + + @Test + public void testSizeGiga() { + testNodeLength(483286324544L, "450.1 GB"); + } + + @Test + public void testSizeTera() { + testNodeLength(6963696737140L, "6.3 TB"); + } + + @Test + public void testSizePeta() { + testNodeLength(6963696737150000L, "6.2 PB"); + } + + private void testNodeLength(long bytes, String expectedText) { + DataNode node = getDataNode(); + setLength(node, bytes); + NodeInfo nodeInfo = new NodeInfo(node, AUTHORITY); + assertEquals(expectedText, nodeInfo.getSize()); + } + + private DataNode getDataNode() { + DataNode node = new DataNode(); + node.setUri("vos://example.com!vospace/mynode"); + return node; + } + + private void setLength(Node node, long length) { + Property lengthProperty = new Property(); + lengthProperty.setUri("ivo://ivoa.net/vospace/core#length"); + lengthProperty.setValue(String.valueOf(length)); + node.getProperties().add(lengthProperty); + } +} diff --git a/vospace-ui-frontend/src/App.vue b/vospace-ui-frontend/src/App.vue index 9af1920..566eb28 100644 --- a/vospace-ui-frontend/src/App.vue +++ b/vospace-ui-frontend/src/App.vue @@ -20,6 +20,7 @@ <script> import { mapState } from 'vuex' import TopMenu from './components/TopMenu.vue' +import client from 'api-client' export default { name: 'App', @@ -32,6 +33,7 @@ export default { mounted() { this.$store.dispatch('loadJobs'); this.$store.dispatch('loadUserInfo'); + setInterval(client.keepalive, 60000); } } </script> @@ -78,4 +80,19 @@ export default { height: 100%; width: 100%; } + +.node-busy { + position: relative; + padding-right: 3px; +} + +.node-busy .spinner-border { + position: absolute; + left: -5px; + font-size: 7px; + height: 26px; + width: 26px; + top: -7px; + color: #3293f2; +} </style> diff --git a/vospace-ui-frontend/src/api/mock/index.js b/vospace-ui-frontend/src/api/mock/index.js index 9924ec9..e92cd11 100644 --- a/vospace-ui-frontend/src/api/mock/index.js +++ b/vospace-ui-frontend/src/api/mock/index.js @@ -57,5 +57,8 @@ export default { }, deleteNode() { return fetch({}); + }, + keepalive() { + return fetch({}); } } diff --git a/vospace-ui-frontend/src/api/server/index.js b/vospace-ui-frontend/src/api/server/index.js index 32fc75c..d793121 100644 --- a/vospace-ui-frontend/src/api/server/index.js +++ b/vospace-ui-frontend/src/api/server/index.js @@ -136,5 +136,16 @@ export default { 'Cache-Control': 'no-cache' } }, true, true); + }, + keepalive() { + let url = BASE_API_URL + 'keepalive'; + return apiRequest({ + method: 'GET', + url: url, + withCredentials: true, + headers: { + 'Cache-Control': 'no-cache' + } + }, false, false); } } diff --git a/vospace-ui-frontend/src/assets/css/fonts.css b/vospace-ui-frontend/src/assets/css/fonts.css index d24f83f..1041dbd 100644 --- a/vospace-ui-frontend/src/assets/css/fonts.css +++ b/vospace-ui-frontend/src/assets/css/fonts.css @@ -32,7 +32,3 @@ .folder-link-icon { background-image: url("data:image/svg+xml,%3Csvg data-v-41be6633='' viewBox='0 0 16 16' width='1em' height='1em' focusable='false' role='img' aria-label='folder symlink' xmlns='http://www.w3.org/2000/svg' fill='currentColor' class='bi-folder-symlink mx-auto b-icon bi'%3E%3Cg data-v-41be6633=''%3E%3Cpath d='M11.798 8.271l-3.182 1.97c-.27.166-.616-.036-.616-.372V9.1s-2.571-.3-4 2.4c.571-4.8 3.143-4.8 4-4.8v-.769c0-.336.346-.538.616-.371l3.182 1.969c.27.166.27.576 0 .742z'%3E%3C/path%3E%3Cpath d='M.5 3l.04.87a1.99 1.99 0 0 0-.342 1.311l.637 7A2 2 0 0 0 2.826 14h10.348a2 2 0 0 0 1.991-1.819l.637-7A2 2 0 0 0 13.81 3H9.828a2 2 0 0 1-1.414-.586l-.828-.828A2 2 0 0 0 6.172 1H2.5a2 2 0 0 0-2 2zm.694 2.09A1 1 0 0 1 2.19 4h11.62a1 1 0 0 1 .996 1.09l-.636 7a1 1 0 0 1-.996.91H2.826a1 1 0 0 1-.995-.91l-.637-7zM6.172 2a1 1 0 0 1 .707.293L7.586 3H2.19c-.24 0-.47.042-.684.12L1.5 2.98a1 1 0 0 1 1-.98h3.672z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E"); } - -.gear-icon { - background-image: url("data:image/svg+xml,%3Csvg data-v-41be6633='' viewBox='0 0 16 16' width='1em' height='1em' focusable='false' role='img' aria-label='gear' xmlns='http://www.w3.org/2000/svg' fill='currentColor' class='bi-gear mx-auto b-icon bi'%3E%3Cg data-v-41be6633=''%3E%3Cpath d='M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z'%3E%3C/path%3E%3Cpath d='M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z'%3E%3C/path%3E%3C/g%3E%3C/svg%3E"); -} -- GitLab