You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2019/08/29 14:39:08 UTC

[airavata-django-portal] branch master updated: AIRAVATA-3081 Integrate tus uploads with storage views

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git


The following commit(s) were added to refs/heads/master by this push:
     new 2546acc  AIRAVATA-3081 Integrate tus uploads with storage views
2546acc is described below

commit 2546acc8ae2e598ea42abfde309190552d042b27
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu Aug 29 10:38:54 2019 -0400

    AIRAVATA-3081 Integrate tus uploads with storage views
---
 django_airavata/apps/api/data_products_helper.py   |  8 ++++---
 django_airavata/apps/api/views.py                  | 19 +++++++++++++++
 .../components/experiment/input-editors/Uppy.vue   | 26 +++++++++++++--------
 .../js/containers/UserStorageContainer.vue         | 27 +++++++++++++++-------
 4 files changed, 60 insertions(+), 20 deletions(-)

diff --git a/django_airavata/apps/api/data_products_helper.py b/django_airavata/apps/api/data_products_helper.py
index 3a07699..1614acd 100644
--- a/django_airavata/apps/api/data_products_helper.py
+++ b/django_airavata/apps/api/data_products_helper.py
@@ -19,11 +19,12 @@ logger = logging.getLogger(__name__)
 TMP_INPUT_FILE_UPLOAD_DIR = "tmp"
 
 
-def save(request, path, file):
+def save(request, path, file, name=None):
     "Save file in path in the user's storage."
+    logger.debug("save name=" + name)
     username = request.user.username
-    full_path = datastore.save(username, path, file)
-    data_product = _save_data_product(request, full_path)
+    full_path = datastore.save(username, path, file, name=name)
+    data_product = _save_data_product(request, full_path, name=name)
     return data_product
 
 
@@ -178,6 +179,7 @@ def _get_data_product_uri(request, full_path):
 
 def _save_data_product(request, full_path, name=None):
     "Create, register and record in DB a data product for full_path."
+    logger.debug("_save_data_product name=" + name)
     data_product = _create_data_product(
         request.user.username, full_path, name=name)
     product_uri = request.airavata_client.registerDataProduct(
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 866529a..9b0246b 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -1454,10 +1454,29 @@ class UserStoragePathView(APIView):
             data_products_helper.create_user_dir(request, path)
 
         data_product = None
+        # Handle direct upload
         if 'file' in request.FILES:
             user_file = request.FILES['file']
             data_product = data_products_helper.save(
                 request, path, user_file)
+        # Handle a tus upload
+        elif 'uploadURL' in request.POST:
+            # TODO: factor out tus uploadURL parsing, retrieval
+            uploadURL = request.POST['uploadURL']
+            # file UUID is last path component in URL. For example:
+            # http://localhost:1080/files/2c44415fdb6259a22f425145b87d0840
+            upload_uuid = urlparse(uploadURL).path.split("/")[-1]
+            upload_bin_path = os.path.join(settings.TUS_DATA_DIR,
+                                           f"{upload_uuid}.bin")
+            log.debug(f"upload_bin_path={upload_bin_path}")
+            upload_info_path = os.path.join(settings.TUS_DATA_DIR,
+                                            f"{upload_uuid}.info")
+            with open(upload_info_path) as upload_info_file, \
+                    open(upload_bin_path, "rb") as upload_file:
+                upload_info = json.load(upload_info_file)
+                filename = upload_info['MetaData']['filename']
+                data_product = data_products_helper.save(
+                    request, path, upload_file, name=filename)
         return self._create_response(request, path, uploaded=data_product)
 
     def delete(self, request, path="/", format=None):
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue
index f77ca01..98c96c5 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue
@@ -43,23 +43,28 @@ export default {
   destroyed() {
     // TODO: tear down the Uppy instance
   },
+  data() {
+    return {
+      uppy: null
+    }
+  },
   methods: {
     initUppy() {
-      const uppy = Uppy({
+      this.uppy = Uppy({
         // TODO: set id
         autoProceed: true,
         // TODO: add maxFileSize restriction
         debug: true
       });
-      uppy.use(FileInput, { target: this.$refs.fileInput, pretty: false });
-      uppy.use(StatusBar, {
+      this.uppy.use(FileInput, { target: this.$refs.fileInput, pretty: false });
+      this.uppy.use(StatusBar, {
         target: this.$refs.statusBar,
         hideUploadButton: true,
         hideAfterFinish: false
       });
       if (this.settings.tusEndpoint) {
-        uppy.use(Tus, { endpoint: this.settings.tusEndpoint });
-        uppy.on("upload-success", (file, response) => {
+        this.uppy.use(Tus, { endpoint: this.settings.tusEndpoint });
+        this.uppy.on("upload-success", (file, response) => {
           const data = new FormData();
           data.append("uploadURL", response.uploadURL);
           utils.FetchUtils.post(this.tusUploadFinishEndpoint, data, "", {
@@ -69,7 +74,7 @@ export default {
           });
         });
       } else {
-        uppy.use(XHRUpload, {
+        this.uppy.use(XHRUpload, {
           endpoint: this.xhrUploadEndpoint,
           withCredentials: true,
           headers: {
@@ -77,16 +82,19 @@ export default {
           },
           fieldName: 'file'
         });
-        uppy.on("upload-success", (file, response) => {
+        this.uppy.on("upload-success", (file, response) => {
           this.$emit("upload-success", response.body);
         });
       }
-      uppy.on("upload", () => {
+      this.uppy.on("upload", () => {
         this.$emit("upload-started");
       });
-      uppy.on("complete", () => {
+      this.uppy.on("complete", () => {
         this.$emit("upload-finished");
       });
+    },
+    reset() {
+      this.uppy.reset();
     }
   }
 };
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
index 567de2e..a47ff71 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
@@ -17,14 +17,12 @@
           :state="fileUploadState"
           :invalid-feedback="fileUploadInvalidFeedback"
         >
-          <b-form-file
-            v-model="file"
-            ref="file-input"
-            placeholder="Add file"
-            @input="fileChanged"
-            class="mb-2"
-            :state="fileUploadState"
-          ></b-form-file>
+          <uppy
+            ref="file-upload"
+            :xhr-upload-endpoint="uploadEndpoint"
+            :tus-upload-finish-endpoint="uploadEndpoint"
+            @upload-success="uploadSuccess"
+          />
         </b-form-group>
       </div>
       <div class="col">
@@ -62,8 +60,13 @@
 import { services, session, utils } from "django-airavata-api";
 import { notifications } from "django-airavata-common-ui";
 
+import Uppy from "../components/experiment/input-editors/Uppy";
+
 export default {
   name: "user-storage-container",
+  components: {
+    Uppy
+  },
   computed: {
     storagePath() {
       if (this.$route.path.startsWith("/")) {
@@ -114,6 +117,10 @@ export default {
       } else {
         return null;
       }
+    },
+    uploadEndpoint() {
+      // This endpoint can handle XHR upload or a TUS uploadURL
+      return "/api/user-storage/" + this.storagePath;
     }
   },
   data() {
@@ -166,6 +173,10 @@ export default {
         });
       }
     },
+    uploadSuccess() {
+      this.$refs["file-upload"].reset();
+      this.loadUserStoragePath(this.storagePath);
+    },
     addDirectory() {
       if (this.dirName) {
         let newDirPath = this.storagePath;