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 2021/09/15 19:32:00 UTC

[airavata-django-portal] branch develop updated: AIRAVATA-3510 When getApplicationInterface fails, check to see if it failed because it no longer exists, and return a 404

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new f5160a2  AIRAVATA-3510 When getApplicationInterface fails, check to see if it failed because it no longer exists, and return a 404
f5160a2 is described below

commit f5160a202afaadd441023df42864280aca83d522
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Wed Sep 15 15:30:34 2021 -0400

    AIRAVATA-3510 When getApplicationInterface fails, check to see if it failed because it no longer exists, and return a 404
---
 .../django_airavata_api/js/errors/ErrorUtils.js    |   3 +
 django_airavata/apps/api/views.py                  |  14 +-
 .../js/containers/EditExperimentContainer.vue      |  13 +-
 .../js/containers/ExperimentListContainer.vue      | 158 ++++++++++++---------
 .../js/containers/RecentExperimentsContainer.vue   |  13 +-
 .../common/js/components/ApplicationName.vue       |  11 +-
 6 files changed, 130 insertions(+), 82 deletions(-)

diff --git a/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorUtils.js b/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorUtils.js
index e4f62bb..7832993 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorUtils.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorUtils.js
@@ -19,4 +19,7 @@ export default {
   isUnauthorizedError(error) {
     return this.isAPIException(error) && error.details.status === 403;
   },
+  isNotFoundError(error) {
+    return this.isAPIException(error) && error.details.status === 404;
+  },
 };
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index d8ec2c8..0bcc714 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -700,8 +700,18 @@ class ApplicationInterfaceViewSet(APIBackedViewSet):
             self.authz_token, self.gateway_id)
 
     def get_instance(self, lookup_value):
-        return self.request.airavata_client.getApplicationInterface(
-            self.authz_token, lookup_value)
+        try:
+            return self.request.airavata_client.getApplicationInterface(
+                self.authz_token, lookup_value)
+        except Exception:
+            # If it failed to load, check to see if it exists at all
+            all_interfaces = self.request.airavata_client.getAllApplicationInterfaces(
+                self.authz_token, self.gateway_id)
+            interface_ids = map(lambda i: i.applicationInterfaceId, all_interfaces)
+            if lookup_value not in interface_ids:
+                raise Http404("Application interface does not exist")
+            else:
+                raise  # re-raise
 
     def perform_create(self, serializer):
         application_interface = serializer.save()
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue
index 6a71d43..5729c91 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/EditExperimentContainer.vue
@@ -11,7 +11,7 @@
 </template>
 
 <script>
-import { services } from "django-airavata-api";
+import { errors, services } from "django-airavata-api";
 import { notifications } from "django-airavata-common-ui";
 import ExperimentEditor from "../components/experiment/ExperimentEditor.vue";
 import urls from "../utils/urls";
@@ -67,14 +67,15 @@ export default {
       .then((appModule) => {
         this.appModule = appModule;
       })
-      .catch(() => {
+      .catch((error) => {
+        const message = errors.ErrorUtils.isNotFoundError(error)
+          ? `Application interface (${this.experiment.executionId}) was not found.
+           If it has been deleted then you won't be able to edit this experiment.`
+          : `Unable to load application interface (${this.experiment.executionId}) or module`;
         notifications.NotificationList.add(
           new notifications.Notification({
             type: "ERROR",
-            message:
-              "Unable to load application interface (" +
-              this.experiment.executionId +
-              ") or module. If it has been deleted then you won't be able to edit this experiment.",
+            message,
           })
         );
       });
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
index 39a98b1..d243578 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/ExperimentListContainer.vue
@@ -8,31 +8,44 @@
     <div class="row">
       <div class="col">
         <div class="card">
-          <div class = "card-body">
+          <div class="card-body">
             <b-input-group class="w-100 mb-2">
-              <b-form-input v-if="defaultOptionSelected" v-model="search" 
+              <b-form-input
+                v-if="defaultOptionSelected"
+                v-model="search"
                 placeholder="Search Experiments"
                 @keydown.native.enter="searchExperiments"
               />
-              <b-form-select v-if="applicationSelected" v-model="applicationSelect" 
-              :options="applicationNameOptions"
+              <b-form-select
+                v-if="applicationSelected"
+                v-model="applicationSelect"
+                :options="applicationNameOptions"
               >
                 <template slot="first">
-                  <option :value="null" disabled>Select an application to search by</option>
+                  <option :value="null" disabled>
+                    Select an application to search by
+                  </option>
                 </template>
               </b-form-select>
-              <b-form-select v-if="projectSelected" v-model="projectSelect" 
-              :options="projectNameOptions"
+              <b-form-select
+                v-if="projectSelected"
+                v-model="projectSelect"
+                :options="projectNameOptions"
               >
                 <template slot="first">
-                  <option :value="null" disabled>Select a project to search by</option>
+                  <option :value="null" disabled>
+                    Select a project to search by
+                  </option>
                 </template>
               </b-form-select>
-              <b-form-select v-model="experimentAttributeSelect" 
-              @input="checkSearchOptions"
+              <b-form-select
+                v-model="experimentAttributeSelect"
+                @input="checkSearchOptions"
               >
                 <template slot="first">
-                  <option :value="null" disabled>Select an attribute to search by</option>
+                  <option :value="null" disabled>
+                    Select an attribute to search by
+                  </option>
                 </template>
                 <option value="USER_NAME">User Name</option>
                 <option value="EXPERIMENT_NAME">Experiment Name</option>
@@ -43,7 +56,9 @@
               </b-form-select>
               <b-form-select v-model="experimentStatusSelect">
                 <template slot="first">
-                  <option :value="null" disabled>Select an experiment status to filter by</option>
+                  <option :value="null" disabled>
+                    Select an experiment status to filter by
+                  </option>
                 </template>
                 <option value="ALL">ALL</option>
                 <option value="CREATED">Created</option>
@@ -57,25 +72,22 @@
               </b-form-select>
               <b-input-group-append>
                 <b-button @click="resetSearch">Reset</b-button>
-                <b-button
-                  variant="primary"
-                  @click="searchExperiments"
-                >Search</b-button>
+                <b-button variant="primary" @click="searchExperiments"
+                  >Search</b-button
+                >
               </b-input-group-append>
             </b-input-group>
             <b-input-group class="w-100 mb-2">
-            <b-input-group-prepend is-text >
-                    <i
-                      class="fa fa-calendar-week"
-                      aria-hidden="true"
-                    ></i>
-                  </b-input-group-prepend>
-                  <flat-pickr v-model="dateSelect"          
-                    :config="dateConfig"
-                    placeholder="Select a date range to filter by"
-                    @on-change="dateRangeChanged"
-                    class="form-control"
-                  />            
+              <b-input-group-prepend is-text>
+                <i class="fa fa-calendar-week" aria-hidden="true"></i>
+              </b-input-group-prepend>
+              <flat-pickr
+                v-model="dateSelect"
+                :config="dateConfig"
+                placeholder="Select a date range to filter by"
+                @on-change="dateRangeChanged"
+                class="form-control"
+              />
             </b-input-group>
           </div>
         </div>
@@ -160,7 +172,7 @@
 </template>
 
 <script>
-import { models, services, utils } from "django-airavata-api";
+import { errors, models, services, utils } from "django-airavata-api";
 import { components as comps } from "django-airavata-common-ui";
 
 import moment from "moment";
@@ -173,18 +185,18 @@ export default {
     return {
       experimentsPaginator: null,
       applicationInterfaces: {},
-      search: null, 
-      applicationSelect: null, 
+      search: null,
+      applicationSelect: null,
       projectSelect: null,
-      dateSelect : null,
-      experimentAttributeSelect: null, 
-      experimentStatusSelect: null, 
+      dateSelect: null,
+      experimentAttributeSelect: null,
+      experimentStatusSelect: null,
       appInterfaces: null,
       projectInterfaces: null,
       fromDate: null,
       toDate: null,
       applicationSelected: false,
-      projectSelected: false, 
+      projectSelected: false,
       defaultOptionSelected: true,
       dateConfig: {
         mode: "range",
@@ -199,11 +211,11 @@ export default {
     "experiment-status-badge": comps.ExperimentStatusBadge,
   },
   methods: {
-    searchExperiments: function(){
+    searchExperiments: function () {
       this.experimentsPaginator = null;
       this.reloadExperiments();
     },
-    resetSearch: function(){
+    resetSearch: function () {
       this.experimentsPaginator = null;
       this.search = null;
       this.experimentAttributeSelect = null;
@@ -216,58 +228,60 @@ export default {
       this.checkSearchOptions();
       this.reloadExperiments();
     },
-    reloadExperiments: function(){
+    reloadExperiments: function () {
       const searchParams = {};
       if (this.experimentAttributeSelect) {
-        if (this.experimentAttributeSelect=="APPLICATION_ID"&& this.applicationSelect){
+        if (
+          this.experimentAttributeSelect == "APPLICATION_ID" &&
+          this.applicationSelect
+        ) {
           searchParams["APPLICATION_ID"] = this.applicationSelect;
-        }
-        else if (this.experimentAttributeSelect=="PROJECT_ID"&& this.projectSelect){
+        } else if (
+          this.experimentAttributeSelect == "PROJECT_ID" &&
+          this.projectSelect
+        ) {
           searchParams["PROJECT_ID"] = this.projectSelect;
-        }
-        else if (this.search){
+        } else if (this.search) {
           searchParams[this.experimentAttributeSelect] = this.search;
         }
       }
-      if (this.experimentStatusSelect){
-        if (this.experimentStatusSelect != "ALL"){
-          searchParams["STATUS"]= this.experimentStatusSelect;
+      if (this.experimentStatusSelect) {
+        if (this.experimentStatusSelect != "ALL") {
+          searchParams["STATUS"] = this.experimentStatusSelect;
         }
       }
-      if (this.fromDate && this.toDate){
+      if (this.fromDate && this.toDate) {
         searchParams["FROM_DATE"] = this.fromDate.getTime();
         searchParams["TO_DATE"] = this.toDate.getTime();
       }
 
       services.ExperimentSearchService.list(searchParams).then(
-        result => (this.experimentsPaginator = result)
-        );
+        (result) => (this.experimentsPaginator = result)
+      );
     },
-    checkSearchOptions: function(){
+    checkSearchOptions: function () {
       this.applicationSelected = false;
       this.projectSelected = false;
       this.defaultOptionSelected = false;
-      if(this.experimentAttributeSelect == "APPLICATION_ID"){
+      if (this.experimentAttributeSelect == "APPLICATION_ID") {
         this.applicationSelected = true;
-      }
-      else if( this.experimentAttributeSelect == "PROJECT_ID"){
-        this.projectSelected=true;
-      }
-      else{
+      } else if (this.experimentAttributeSelect == "PROJECT_ID") {
+        this.projectSelected = true;
+      } else {
         this.defaultOptionSelected = true;
       }
     },
-    loadApplicationInterfaces: function() {
+    loadApplicationInterfaces: function () {
       return services.ApplicationInterfaceService.list().then(
         (appInterfaces) => (this.appInterfaces = appInterfaces)
       );
     },
-    loadProjectInterfaces: function(){
+    loadProjectInterfaces: function () {
       return services.ProjectService.listAll().then(
         (projectInterfaces) => (this.projectInterfaces = projectInterfaces)
       );
     },
-    dateRangeChanged: function(selectedDates) {
+    dateRangeChanged: function (selectedDates) {
       [this.fromDate, this.toDate] = selectedDates;
       if (this.fromDate && this.toDate) {
         this.reloadExperiments();
@@ -310,17 +324,25 @@ export default {
             ignoreErrors: true,
           }
         )
-          .then((result) =>
+          .then((result) => {
             this.$set(
               this.applicationInterfaces,
               experiment.executionId,
               result
-            )
-          )
-          .catch(() => {
-            // Application interface may be deleted
-            this.$set(this.applicationInterfaces, experiment.executionId, null);
-          });
+            );
+          })
+          .catch((error) => {
+            if (errors.ErrorUtils.isNotFoundError(error)) {
+              this.$set(
+                this.applicationInterfaces,
+                experiment.executionId,
+                null
+              );
+            } else {
+              throw error;
+            }
+          })
+          .catch(utils.FetchUtils.reportError);
         this.$set(this.applicationInterfaces, experiment.executionId, request);
       }
       return "...";
@@ -353,7 +375,7 @@ export default {
       }
     },
     projectNameOptions() {
-      if (this.projectInterfaces){
+      if (this.projectInterfaces) {
         const options = this.projectInterfaces.map((projectInterface) => {
           return {
             value: projectInterface.projectID,
@@ -361,7 +383,7 @@ export default {
           };
         });
         return utils.StringUtils.sortIgnoreCase(options, (o) => o.text);
-      } else{
+      } else {
         return [];
       }
     },
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/RecentExperimentsContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/RecentExperimentsContainer.vue
index 0001d35..84ad18d 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/RecentExperimentsContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/RecentExperimentsContainer.vue
@@ -18,7 +18,7 @@
 
 <script>
 import urls from "../utils/urls";
-import { models, services } from "django-airavata-api";
+import { errors, models, services, utils } from "django-airavata-api";
 import { components } from "django-airavata-common-ui";
 export default {
   name: "recent-experiments-container",
@@ -103,10 +103,15 @@ export default {
         .then((applicationInterface) => {
           this.applicationInterfaces[interfaceId] = applicationInterface;
         })
-        .catch(() => {
+        .catch((error) => {
           // ignore if missing
-          this.applicationInterfaces[interfaceId] = null;
-        });
+          if (errors.ErrorUtils.isNotFoundError(error)) {
+            this.applicationInterfaces[interfaceId] = null;
+          } else {
+            throw error;
+          }
+        })
+        .catch(utils.FetchUtils.reportError);
     },
     populateApplicationNames() {
       this.feedItems
diff --git a/django_airavata/static/common/js/components/ApplicationName.vue b/django_airavata/static/common/js/components/ApplicationName.vue
index 6fc425b..df0c95e 100644
--- a/django_airavata/static/common/js/components/ApplicationName.vue
+++ b/django_airavata/static/common/js/components/ApplicationName.vue
@@ -2,7 +2,7 @@
   <span :class="{ 'font-italic': notAvailable }">{{ applicationName }}</span>
 </template>
 <script>
-import { services } from "django-airavata-api";
+import { errors, services, utils } from "django-airavata-api";
 export default {
   name: "application-name",
   props: {
@@ -27,7 +27,14 @@ export default {
         { ignoreErrors: true, cache: true }
       )
         .then((appInterface) => (this.applicationInterface = appInterface))
-        .catch(() => (this.notAvailable = true));
+        .catch((error) => {
+          if (errors.ErrorUtils.isNotFoundError(error)) {
+            this.notAvailable = true;
+          } else {
+            throw error;
+          }
+        })
+        .catch(utils.FetchUtils.reportError);
     },
   },
   computed: {