From ae7d5a44d881ea22feb2c076d56ea6d102e59f10 Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Thu, 21 Jan 2021 16:05:05 +0100
Subject: [PATCH] Renamed recallFromTape to AsyncRecall, handled icons and
 button visibility

---
 .../inaf/ia2/vospace/ui/service/NodeInfo.java | 25 ++++++++++++++-----
 .../ia2/vospace/ui/service/NodesService.java  | 16 +++++++++---
 .../src/api/mock/data/nodes/folder1.html      |  4 +--
 .../src/api/mock/data/nodes/folder2.html      |  4 +--
 vospace-ui-frontend/src/api/mock/index.js     |  2 +-
 vospace-ui-frontend/src/api/server/index.js   |  2 +-
 vospace-ui-frontend/src/components/Main.vue   | 10 ++++----
 vospace-ui-frontend/src/store.js              | 18 ++++++-------
 8 files changed, 52 insertions(+), 29 deletions(-)

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 74254a4..82d66fd 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
@@ -13,24 +13,25 @@ public class NodeInfo {
 
     private final String authority;
 
-    private final Node node;
-
     private final String path;
     private final String name;
     private final String size;
+    private final String type;
     private final String groupRead;
     private final String groupWrite;
     private final boolean isPublic;
+    private final boolean asyncTrans;
 
     public NodeInfo(Node node, String authority) {
         this.authority = authority;
-        this.node = node;
         this.path = getPath(node);
         this.name = path.substring(path.lastIndexOf("/") + 1);
         this.size = getSize(node);
+        this.type = node.getType();
         this.groupRead = getGroupRead(node);
         this.groupWrite = getGroupWrite(node);
         this.isPublic = isPublic(node);
+        this.asyncTrans = isAsyncTrans(node);
     }
 
     private String getPath(Node node) {
@@ -55,7 +56,11 @@ public class NodeInfo {
     }
 
     private boolean isPublic(Node node) {
-        return Boolean.parseBoolean(getProperty(node, "ivo://ivoa.net/vospace/core#ispublic").orElse("false"));
+        return getProperty(node, "ivo://ivoa.net/vospace/core#ispublic").map(value -> "t".equals(value)).orElse(false);
+    }
+
+    private boolean isAsyncTrans(Node node) {
+        return getProperty(node, "urn:async_trans").map(value -> "t".equals(value)).orElse(false);
     }
 
     private Optional<String> getProperty(Node node, String uri) {
@@ -97,8 +102,12 @@ public class NodeInfo {
         return String.format("%.1f %cB", bytes / 1024f, " kMGTPE".charAt(u));
     }
 
-    public String getType() {
-        return node.getType();
+    public boolean isFolder() {
+        return "vos:ContainerNode".equals(type);
+    }
+
+    public boolean isFile() {
+        return !"vos:ContainerNode".equals(type);
     }
 
     public String getPath() {
@@ -124,4 +133,8 @@ public class NodeInfo {
     public boolean isPublic() {
         return isPublic;
     }
+
+    public boolean isAsyncTrans() {
+        return asyncTrans;
+    }
 }
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 8f5e6c6..b7aa7fb 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
@@ -54,7 +54,11 @@ public class NodesService {
         }
 
         String html = "<tr>";
-        html += "<td><input type=\"checkbox\" data-node=\"" + nodeInfo.getPath() + "\" /></td>";
+        html += "<td><input type=\"checkbox\" data-node=\"" + nodeInfo.getPath() + "\" ";
+        if (nodeInfo.isAsyncTrans()) {
+            html += "class=\"async\"";
+        }
+        html += "/></td>";
         html += "<td>" + getIcon(nodeInfo) + getLink(nodeInfo) + "</td>";
         html += "<td>" + nodeInfo.getSize() + "</td>";
         html += "<td>" + nodeInfo.getGroupRead() + "</td>";
@@ -65,18 +69,21 @@ public class NodesService {
 
     private String getIcon(NodeInfo nodeInfo) {
         String html = "<span class=\"icon ";
-        if ("vos:ContainerNode".equals(nodeInfo.getType())) {
+        if (nodeInfo.isFolder()) {
             html += "folder";
         } else {
             html += "file";
         }
+        if (nodeInfo.isAsyncTrans()) {
+            html += "-x";
+        }
         html += "-icon\"></span>&nbsp;";
         return html;
     }
 
     private String getLink(NodeInfo nodeInfo) {
         if (isDownloadable(nodeInfo)) {
-            if ("vos:ContainerNode".equals(nodeInfo.getType())) {
+            if (nodeInfo.isFolder()) {
                 return "<a href=\"#/nodes" + nodeInfo.getPath() + "\">" + nodeInfo.getName() + "</a>";
             } else {
                 return "<a href=\"download" + nodeInfo.getPath() + "\" target=\"blank_\">" + nodeInfo.getName() + "</a>";
@@ -86,6 +93,9 @@ public class NodesService {
     }
 
     private boolean isDownloadable(NodeInfo nodeInfo) {
+        if (nodeInfo.isFile() && nodeInfo.isAsyncTrans()) {
+            return false;
+        }
         if (nodeInfo.isPublic()) {
             return true;
         }
diff --git a/vospace-ui-frontend/src/api/mock/data/nodes/folder1.html b/vospace-ui-frontend/src/api/mock/data/nodes/folder1.html
index ee2b379..3b878bd 100644
--- a/vospace-ui-frontend/src/api/mock/data/nodes/folder1.html
+++ b/vospace-ui-frontend/src/api/mock/data/nodes/folder1.html
@@ -1,6 +1,6 @@
 <tbody id="nodes">
   <tr>
-    <td><input type="checkbox" class="tape" data-node="/folder1/folder2" /></td>
+    <td><input type="checkbox" class="async" data-node="/folder1/folder2" /></td>
     <td>
       <span class="icon folder-x-icon"></span>
       <a href="#/nodes/folder1/folder2">folder2</a>
@@ -20,7 +20,7 @@
     <td>group2</td>
   </tr>
   <tr>
-    <td><input type="checkbox" class="tape" data-node="/folder1/file3" /></td>
+    <td><input type="checkbox" class="async" data-node="/folder1/file3" /></td>
     <td>
       <span class="icon file-x-icon"></span>
       file3
diff --git a/vospace-ui-frontend/src/api/mock/data/nodes/folder2.html b/vospace-ui-frontend/src/api/mock/data/nodes/folder2.html
index b861b45..24ac8b8 100644
--- a/vospace-ui-frontend/src/api/mock/data/nodes/folder2.html
+++ b/vospace-ui-frontend/src/api/mock/data/nodes/folder2.html
@@ -1,6 +1,6 @@
 <tbody id="nodes">
   <tr>
-    <td><input type="checkbox" class="tape" data-node="/folder1/folder2/file4" /></td>
+    <td><input type="checkbox" class="async" data-node="/folder1/folder2/file4" /></td>
     <td>
       <span class="icon file-x-icon"></span>
       file4
@@ -10,7 +10,7 @@
     <td>group2</td>
   </tr>
   <tr>
-    <td><input type="checkbox" class="tape" data-node="/folder1/folder2/file5" /></td>
+    <td><input type="checkbox" class="async" data-node="/folder1/folder2/file5" /></td>
     <td>
       <span class="icon file-x-icon"></span>
       file5
diff --git a/vospace-ui-frontend/src/api/mock/index.js b/vospace-ui-frontend/src/api/mock/index.js
index c0e1079..7130ca5 100644
--- a/vospace-ui-frontend/src/api/mock/index.js
+++ b/vospace-ui-frontend/src/api/mock/index.js
@@ -37,7 +37,7 @@ export default {
     }
     return fetch(response);
   },
-  startRecallFromTapeJob() {
+  startAsyncRecallJob() {
     return fetch(job);
   },
   loadJobs() {
diff --git a/vospace-ui-frontend/src/api/server/index.js b/vospace-ui-frontend/src/api/server/index.js
index dcba84e..9284551 100644
--- a/vospace-ui-frontend/src/api/server/index.js
+++ b/vospace-ui-frontend/src/api/server/index.js
@@ -73,7 +73,7 @@ export default {
       }
     }, false, false);
   },
-  startRecallFromTapeJob(paths) {
+  startAsyncRecallJob(paths) {
     let url = BASE_API_URL + 'recall';
     return apiRequest({
       method: 'POST',
diff --git a/vospace-ui-frontend/src/components/Main.vue b/vospace-ui-frontend/src/components/Main.vue
index 8f2ea80..3b8fc6f 100644
--- a/vospace-ui-frontend/src/components/Main.vue
+++ b/vospace-ui-frontend/src/components/Main.vue
@@ -4,7 +4,7 @@
   <div class="mb-3">
     <b-button variant="success" class="mr-2" :disabled="false" v-b-modal.create-folder-modal>New folder</b-button>
     <b-button variant="success" class="mr-2" :disabled="false" v-b-modal.upload-files-modal>Upload files</b-button>
-    <b-button variant="primary" class="mr-2" v-if="tapeButtonEnabled" @click="startRecallFromTapeJob">Recall from tape</b-button>
+    <b-button variant="primary" class="mr-2" v-if="asyncButtonEnabled" @click="startAsyncRecallJob">Async recall</b-button>
   </div>
   <b-card>
     <table class="table b-table table-striped table-hover">
@@ -62,8 +62,8 @@ export default {
       }
       return items;
     },
-    tapeButtonEnabled() {
-      return this.$store.state.tapeButtonEnabled || true; // temporary always true
+    asyncButtonEnabled() {
+      return this.$store.state.asyncButtonEnabled;
     }
   },
   created() {
@@ -88,8 +88,8 @@ export default {
         this.$store.dispatch('computeButtonVisibility');
       });
     },
-    startRecallFromTapeJob() {
-      this.$store.dispatch('startRecallFromTapeJob');
+    startAsyncRecallJob() {
+      this.$store.dispatch('startAsyncRecallJob');
     }
   }
 }
diff --git a/vospace-ui-frontend/src/store.js b/vospace-ui-frontend/src/store.js
index b1b108f..848ce45 100644
--- a/vospace-ui-frontend/src/store.js
+++ b/vospace-ui-frontend/src/store.js
@@ -11,7 +11,7 @@ export default new Vuex.Store({
   state: {
     path: '',
     loading: true,
-    tapeButtonEnabled: false,
+    asyncButtonEnabled: false,
     jobs: [],
     user: 'anonymous'
   },
@@ -25,8 +25,8 @@ export default new Vuex.Store({
       }
       state.path = value;
     },
-    setTapeButtonEnabled(state, value) {
-      state.tapeButtonEnabled = value;
+    setAsyncButtonEnabled(state, value) {
+      state.asyncButtonEnabled = value;
     },
     setJobs(state, jobs) {
       // empty the array
@@ -59,15 +59,15 @@ export default new Vuex.Store({
         });
     },
     computeButtonVisibility({ commit }) {
-      commit('setTapeButtonEnabled', document.querySelectorAll('#nodes input.tape:checked').length > 0);
+      commit('setAsyncButtonEnabled', document.querySelectorAll('#nodes input.async:checked').length > 0);
     },
-    startRecallFromTapeJob({ commit }) {
-      let tapeCheckboxes = document.querySelectorAll('#nodes input:checked'); // temporary: it should be input.tape
+    startAsyncRecallJob({ commit }) {
+      let asyncCheckboxes = document.querySelectorAll('#nodes input.async:checked');
       let paths = [];
-      for (let i = 0; i < tapeCheckboxes.length; i++) {
-        paths.push(tapeCheckboxes[i].getAttribute('data-node'));
+      for (let i = 0; i < asyncCheckboxes.length; i++) {
+        paths.push(asyncCheckboxes[i].getAttribute('data-node'));
       }
-      client.startRecallFromTapeJob(paths)
+      client.startAsyncRecallJob(paths)
         .then(job => {
           main.showInfo('Job started');
           commit('addJob', job);
-- 
GitLab