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 2018/09/24 15:34:16 UTC
[airavata-django-portal] branch master updated: AIRAVATA-2876
Display validation errors when saving app module
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 f8d5c01 AIRAVATA-2876 Display validation errors when saving app module
f8d5c01 is described below
commit f8d5c01c1f2e6ac00dbc6b133cac48bd01aab11d
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Mon Sep 24 11:34:02 2018 -0400
AIRAVATA-2876 Display validation errors when saving app module
---
.../applications/ApplicationEditorContainer.vue | 49 +++--
.../applications/ApplicationModuleEditor.vue | 18 +-
django_airavata/apps/api/exceptions.py | 5 -
django_airavata/apps/api/serializers.py | 4 +-
.../django_airavata_api/js/errors/ErrorUtils.js | 10 +
.../api/static/django_airavata_api/js/index.js | 114 +++++------
.../js/models/ApplicationInterfaceDefinition.js | 47 +++--
.../django_airavata_api/js/utils/FetchUtils.js | 217 ++++++++++++++-------
.../static/common/js/errors/ValidationErrors.js | 27 +++
django_airavata/static/common/js/index.js | 4 +-
10 files changed, 313 insertions(+), 182 deletions(-)
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue
index 583ea8d..d8c1426 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationEditorContainer.vue
@@ -19,7 +19,7 @@
<b-nav-item active-class="active" :to="{name: 'application_deployments', params: {id: id}}" :disabled="!id">Deployments</b-nav-item>
</b-nav>
<router-view name="module" v-if="appModule" v-model="appModule" @input="appModuleIsDirty = true" :readonly="!appModule.userHasWriteAccess"
- />
+ :validation-errors="appModuleValidationErrors" />
<router-view name="interface" v-if="appInterface" v-model="appInterface" @input="appInterfaceIsDirty = true" :readonly="!appInterface.userHasWriteAccess"
/>
<router-view name="deployments" v-if="appDeployments" :deployments="appDeployments" @new="createNewDeployment" @delete="deleteApplicationDeployment"
@@ -72,7 +72,8 @@ export default {
appModuleIsDirty: false,
appInterfaceIsDirty: false,
dirtyAppDeploymentComputeHostIds: [],
- dirtyAppDeploymentSharedEntityComputeHostIds: []
+ dirtyAppDeploymentSharedEntityComputeHostIds: [],
+ appModuleValidationErrors: null
};
},
computed: {
@@ -142,28 +143,40 @@ export default {
});
},
createApplicationModule(appModule) {
- return services.ApplicationModuleService.create({ data: appModule }).then(
- appModule => {
- this.appModuleIsDirty = false;
- this.appModule = appModule;
- return appModule;
- }
+ return services.ApplicationModuleService.create(
+ { data: appModule },
+ { ignoreErrors: true }
);
},
updateApplicationModule(appModule) {
- return services.ApplicationModuleService.update({
- lookup: appModule.appModuleId,
- data: appModule
- }).then(appModule => {
- this.appModuleIsDirty = false;
- this.appModule = appModule;
- return appModule;
- });
+ return services.ApplicationModuleService.update(
+ {
+ lookup: appModule.appModuleId,
+ data: appModule
+ },
+ { ignoreErrors: true }
+ );
},
saveApplicationModule(appModule) {
- return this.id
+ return (this.id
? this.updateApplicationModule(appModule)
- : this.createApplicationModule(appModule);
+ : this.createApplicationModule(appModule)
+ )
+ .then(appModule => {
+ this.appModuleValidationErrors = null;
+ this.appModuleIsDirty = false;
+ this.appModule = appModule;
+ return appModule;
+ })
+ .catch(error => {
+ if (errors.ErrorUtils.isValidationError(error)) {
+ this.appModuleValidationErrors = error.details.response;
+ } else {
+ this.appModuleValidationErrors = null;
+ notifications.NotificationList.addError(error);
+ }
+ return Promise.reject(error);
+ });
},
deleteApplicationModule(appModule) {
const deleteModule = this.id
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue
index 8964c71..2ed5628 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/components/applications/ApplicationModuleEditor.vue
@@ -5,8 +5,9 @@
<h1 class="h4 mb-4">
Application Details
</h1>
- <b-form-group label="Application Name" label-for="application-name">
- <b-form-input id="application-name" type="text" v-model="data.appModuleName" required :disabled="readonly"></b-form-input>
+ <b-form-group label="Application Name" label-for="application-name" :invalid-feedback="validationFeedback.appModuleName.invalidFeedback"
+ :state="validationFeedback.appModuleName.state">
+ <b-form-input id="application-name" type="text" v-model="data.appModuleName" required :disabled="readonly" :state="validationFeedback.appModuleName.state"></b-form-input>
</b-form-group>
<b-form-group label="Application Version" label-for="application-version">
<b-form-input id="application-version" type="text" v-model="data.appModuleVersion" :disabled="readonly"></b-form-input>
@@ -21,7 +22,7 @@
<script>
import { models } from "django-airavata-api";
-import { components } from "django-airavata-common-ui";
+import { components, errors } from "django-airavata-common-ui";
import vmodel_mixin from "../commons/vmodel_mixin";
export default {
@@ -34,11 +35,22 @@ export default {
readonly: {
type: Boolean,
default: false
+ },
+ validationErrors: {
+ type: Object
}
},
components: {
"delete-button": components.DeleteButton
},
+ computed: {
+ validationFeedback() {
+ return errors.ValidationErrors.createValidationFeedback(
+ this.data,
+ this.validationErrors
+ );
+ }
+ },
methods: {
save() {
this.$emit("save");
diff --git a/django_airavata/apps/api/exceptions.py b/django_airavata/apps/api/exceptions.py
index e643f1c..4c217e7 100644
--- a/django_airavata/apps/api/exceptions.py
+++ b/django_airavata/apps/api/exceptions.py
@@ -32,9 +32,4 @@ def custom_exception_handler(exc, context):
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
- if isinstance(exc, serializers.ValidationError):
- # Create a default error message for the validation error
- response.data['detail'] = "ValidationError: {}".format(
- json.dumps(response.data))
-
return response
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index ea014c3..c052a40 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -262,7 +262,9 @@ class ApplicationModuleSerializer(
class InputDataObjectTypeSerializer(
thrift_utils.create_serializer_class(InputDataObjectType)):
- pass
+
+ class Meta:
+ required = ('name',)
class ApplicationInterfaceDescriptionSerializer(
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
new file mode 100644
index 0000000..c7d8f4a
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorUtils.js
@@ -0,0 +1,10 @@
+export default {
+ isValidationError(error) {
+ return (
+ error.details &&
+ error.details.status === 400 &&
+ error.details.response &&
+ !("detail" in error.details.response)
+ );
+ }
+};
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 700b4bd..7a04bee 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -1,58 +1,60 @@
-import UnhandledError from './errors/UnhandledError'
-import UnhandledErrorDispatcher from './errors/UnhandledErrorDispatcher'
-import UnhandledErrorDisplayList from './errors/UnhandledErrorDisplayList'
+import ErrorUtils from "./errors/ErrorUtils";
+import UnhandledError from "./errors/UnhandledError";
+import UnhandledErrorDispatcher from "./errors/UnhandledErrorDispatcher";
+import UnhandledErrorDisplayList from "./errors/UnhandledErrorDisplayList";
-import ApplicationDeploymentDescription from './models/ApplicationDeploymentDescription'
-import ApplicationInterfaceDefinition from './models/ApplicationInterfaceDefinition'
-import ApplicationModule from './models/ApplicationModule'
-import BaseModel from './models/BaseModel'
-import BatchQueue from './models/BatchQueue'
-import BatchQueueResourcePolicy from './models/BatchQueueResourcePolicy'
-import CommandObject from './models/CommandObject'
-import ComputeResourcePolicy from './models/ComputeResourcePolicy'
-import DataType from './models/DataType'
-import Experiment from './models/Experiment'
-import ExperimentState from './models/ExperimentState'
-import FullExperiment from './models/FullExperiment'
-import Group from './models/Group'
-import GroupComputeResourcePreference from './models/GroupComputeResourcePreference'
-import GroupPermission from './models/GroupPermission'
-import GroupResourceProfile from './models/GroupResourceProfile'
-import InputDataObjectType from './models/InputDataObjectType'
-import OutputDataObjectType from './models/OutputDataObjectType'
-import ParallelismType from './models/ParallelismType'
-import Project from './models/Project'
-import ResourcePermissionType from './models/ResourcePermissionType'
-import SetEnvPaths from './models/SetEnvPaths'
-import SharedEntity from './models/SharedEntity'
-import SummaryType from './models/SummaryType'
-import UserPermission from './models/UserPermission'
+import ApplicationDeploymentDescription from "./models/ApplicationDeploymentDescription";
+import ApplicationInterfaceDefinition from "./models/ApplicationInterfaceDefinition";
+import ApplicationModule from "./models/ApplicationModule";
+import BaseModel from "./models/BaseModel";
+import BatchQueue from "./models/BatchQueue";
+import BatchQueueResourcePolicy from "./models/BatchQueueResourcePolicy";
+import CommandObject from "./models/CommandObject";
+import ComputeResourcePolicy from "./models/ComputeResourcePolicy";
+import DataType from "./models/DataType";
+import Experiment from "./models/Experiment";
+import ExperimentState from "./models/ExperimentState";
+import FullExperiment from "./models/FullExperiment";
+import Group from "./models/Group";
+import GroupComputeResourcePreference from "./models/GroupComputeResourcePreference";
+import GroupPermission from "./models/GroupPermission";
+import GroupResourceProfile from "./models/GroupResourceProfile";
+import InputDataObjectType from "./models/InputDataObjectType";
+import OutputDataObjectType from "./models/OutputDataObjectType";
+import ParallelismType from "./models/ParallelismType";
+import Project from "./models/Project";
+import ResourcePermissionType from "./models/ResourcePermissionType";
+import SetEnvPaths from "./models/SetEnvPaths";
+import SharedEntity from "./models/SharedEntity";
+import SummaryType from "./models/SummaryType";
+import UserPermission from "./models/UserPermission";
-import ExperimentService from './services/ExperimentService'
-import ExperimentSearchService from './services/ExperimentSearchService'
-import FullExperimentService from './services/FullExperimentService'
-import ProjectService from './services/ProjectService'
-import GroupService from './services/GroupService'
-import UserProfileService from './services/UserProfileService'
-import CloudJobSubmissionService from './services/CloudJobSubmissionService'
-import GlobusJobSubmissionService from './services/GlobusJobSubmissionService'
-import LocaJobSubmissionService from './services/LocaJobSubmissionService'
-import SshJobSubmissionService from './services/SshJobSubmissionService'
-import UnicoreJobSubmissionService from './services/UnicoreJobSubmissionService'
-import SCPDataMovementService from './services/SCPDataMovementService'
-import GridFTPDataMovementService from './services/GridFTPDataMovementService'
-import UnicoreDataMovementService from './services/UnicoreDataMovementService'
-import ServiceFactory from './services/ServiceFactory'
+import ExperimentService from "./services/ExperimentService";
+import ExperimentSearchService from "./services/ExperimentSearchService";
+import FullExperimentService from "./services/FullExperimentService";
+import ProjectService from "./services/ProjectService";
+import GroupService from "./services/GroupService";
+import UserProfileService from "./services/UserProfileService";
+import CloudJobSubmissionService from "./services/CloudJobSubmissionService";
+import GlobusJobSubmissionService from "./services/GlobusJobSubmissionService";
+import LocaJobSubmissionService from "./services/LocaJobSubmissionService";
+import SshJobSubmissionService from "./services/SshJobSubmissionService";
+import UnicoreJobSubmissionService from "./services/UnicoreJobSubmissionService";
+import SCPDataMovementService from "./services/SCPDataMovementService";
+import GridFTPDataMovementService from "./services/GridFTPDataMovementService";
+import UnicoreDataMovementService from "./services/UnicoreDataMovementService";
+import ServiceFactory from "./services/ServiceFactory";
-import FetchUtils from './utils/FetchUtils'
-import PaginationIterator from './utils/PaginationIterator'
-import StringUtils from './utils/StringUtils'
+import FetchUtils from "./utils/FetchUtils";
+import PaginationIterator from "./utils/PaginationIterator";
+import StringUtils from "./utils/StringUtils";
exports.errors = {
+ ErrorUtils,
UnhandledError,
UnhandledErrorDispatcher,
- UnhandledErrorDisplayList,
-}
+ UnhandledErrorDisplayList
+};
exports.models = {
ApplicationDeploymentDescription,
@@ -79,11 +81,13 @@ exports.models = {
SetEnvPaths,
SharedEntity,
SummaryType,
- UserPermission,
-}
+ UserPermission
+};
exports.services = {
- ApplicationDeploymentService: ServiceFactory.service("ApplicationDeployments"),
+ ApplicationDeploymentService: ServiceFactory.service(
+ "ApplicationDeployments"
+ ),
ApplicationInterfaceService: ServiceFactory.service("ApplicationInterfaces"),
ApplicationModuleService: ServiceFactory.service("ApplicationModules"),
CloudJobSubmissionService,
@@ -104,11 +108,11 @@ exports.services = {
SshJobSubmissionService,
UnicoreDataMovementService,
UnicoreJobSubmissionService,
- UserProfileService,
-}
+ UserProfileService
+};
exports.utils = {
FetchUtils,
PaginationIterator,
- StringUtils,
-}
+ StringUtils
+};
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js
index fe46c61..9ca07f0 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/ApplicationInterfaceDefinition.js
@@ -1,46 +1,45 @@
-import BaseModel from './BaseModel'
-import InputDataObjectType from './InputDataObjectType'
-import OutputDataObjectType from './OutputDataObjectType'
-import DataType from './DataType';
-
+import BaseModel from "./BaseModel";
+import InputDataObjectType from "./InputDataObjectType";
+import OutputDataObjectType from "./OutputDataObjectType";
+import DataType from "./DataType";
const FIELDS = [
- 'applicationInterfaceId',
- 'applicationName',
- 'applicationDescription',
+ "applicationInterfaceId",
+ "applicationName",
+ "applicationDescription",
{
- name: 'applicationModules',
- type: 'string',
- list: true,
+ name: "applicationModules",
+ type: "string",
+ list: true
},
// When saving/updating, the order of the inputs in the applicationInputs
// array determines the 'inputOrder' that will be applied to each input on the
// backend. Updating 'inputOrder' will have no effect.
{
- name: 'applicationInputs',
+ name: "applicationInputs",
type: InputDataObjectType,
list: true,
+ default: BaseModel.defaultNewInstance(Array)
},
{
- name: 'applicationOutputs',
+ name: "applicationOutputs",
type: OutputDataObjectType,
- list: true,
+ list: true
},
{
- name: 'archiveWorkingDirectory',
- type: 'boolean',
- default: false,
+ name: "archiveWorkingDirectory",
+ type: "boolean",
+ default: false
},
{
- name: 'hasOptionalFileInputs',
- type: 'boolean',
- default: false,
+ name: "hasOptionalFileInputs",
+ type: "boolean",
+ default: false
},
- 'userHasWriteAccess',
+ "userHasWriteAccess"
];
export default class ApplicationInterfaceDefinition extends BaseModel {
-
constructor(data = {}) {
super(FIELDS, data);
}
@@ -49,12 +48,12 @@ export default class ApplicationInterfaceDefinition extends BaseModel {
const stdout = new OutputDataObjectType({
name: "Standard-Out",
type: DataType.STDOUT,
- isRequired: true,
+ isRequired: true
});
const stderr = new OutputDataObjectType({
name: "Standard-Error",
type: DataType.STDERR,
- isRequired: true,
+ isRequired: true
});
if (!this.applicationOutputs) {
this.applicationOutputs = [];
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/utils/FetchUtils.js b/django_airavata/apps/api/static/django_airavata_api/js/utils/FetchUtils.js
index c6a796c..391682d 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/utils/FetchUtils.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/utils/FetchUtils.js
@@ -1,165 +1,232 @@
import UnhandledErrorDispatcher from "../errors/UnhandledErrorDispatcher";
var count = 0;
-const parseQueryParams = function (url, queryParams = "") {
- if (queryParams && typeof (queryParams) != "string") {
- queryParams = Object.keys(queryParams).map(key => encodeURIComponent(key) + "=" + encodeURIComponent(queryParams[key])).join("&");
+const parseQueryParams = function(url, queryParams = "") {
+ if (queryParams && typeof queryParams != "string") {
+ queryParams = Object.keys(queryParams)
+ .map(
+ key =>
+ encodeURIComponent(key) + "=" + encodeURIComponent(queryParams[key])
+ )
+ .join("&");
}
if (queryParams && queryParams !== "") {
return url + "?" + queryParams;
} else {
return url;
}
-}
+};
-const setSpinnerDisplay = function (display) {
+const setSpinnerDisplay = function(display) {
let spinner = document.getElementById("airavata-spinner");
spinner.style.display = display;
-}
+};
-const incrementCount = function () {
+const incrementCount = function() {
count++;
if (count == 1) {
setSpinnerDisplay("block");
}
-}
-const decrementCount = function () {
+};
+const decrementCount = function() {
if (count > 0) {
count--;
if (count == 0) {
setSpinnerDisplay("none");
}
}
-}
+};
export default {
- enableSpinner: function () {
-
- },
- disableSpinner: function () {
-
- },
- getCSRFToken: function () {
- var csrfToken = document.cookie.split(';').map(val => val.trim()).filter(val => val.startsWith("csrftoken" + '=')).map(val => val.split("=")[1]);
+ enableSpinner: function() {},
+ disableSpinner: function() {},
+ getCSRFToken: function() {
+ var csrfToken = document.cookie
+ .split(";")
+ .map(val => val.trim())
+ .filter(val => val.startsWith("csrftoken" + "="))
+ .map(val => val.split("=")[1]);
if (csrfToken) {
return csrfToken[0];
} else {
return null;
}
},
- createHeaders: function (contentType = "application/json", accept = "application/json") {
+ createHeaders: function(
+ contentType = "application/json",
+ accept = "application/json"
+ ) {
var csrfToken = this.getCSRFToken();
var headers = new Headers({
"Content-Type": contentType,
- "Accept": accept,
+ Accept: accept
});
if (csrfToken != null) {
- headers.set("X-CSRFToken", csrfToken)
+ headers.set("X-CSRFToken", csrfToken);
}
return headers;
},
- post: function (url, body, queryParams = "", { mediaType = "application/json", ignoreErrors = false } = {}) {
- var headers = this.createHeaders(mediaType)
+ post: function(
+ url,
+ body,
+ queryParams = "",
+ { mediaType = "application/json", ignoreErrors = false } = {}
+ ) {
+ var headers = this.createHeaders(mediaType);
// Browsers automatically handle content type for FormData request bodies
if (body instanceof FormData) {
headers.delete("Content-Type");
}
url = parseQueryParams(url, queryParams);
return this.processFetch(url, {
- method: 'post',
- body: (body instanceof FormData || typeof body === 'string') ? body : JSON.stringify(body),
+ method: "post",
+ body:
+ body instanceof FormData || typeof body === "string"
+ ? body
+ : JSON.stringify(body),
headers: headers,
credentials: "same-origin",
ignoreErrors
});
},
- put: function (url, body, { mediaType = "application/json", ignoreErrors = false } = {}) {
+ put: function(
+ url,
+ body,
+ { mediaType = "application/json", ignoreErrors = false } = {}
+ ) {
var headers = this.createHeaders(mediaType);
return this.processFetch(url, {
- method: 'put',
- body: (body instanceof FormData || typeof body === 'string') ? body : JSON.stringify(body),
+ method: "put",
+ body:
+ body instanceof FormData || typeof body === "string"
+ ? body
+ : JSON.stringify(body),
headers: headers,
credentials: "same-origin",
ignoreErrors
});
},
- get: function (url, queryParams = "", { mediaType = "application/json", ignoreErrors = false } = {}) {
- if (queryParams && typeof (queryParams) != "string") {
- queryParams = Object.keys(queryParams).map(key => encodeURIComponent(key) + "=" + encodeURIComponent(queryParams[key])).join("&")
+ get: function(
+ url,
+ queryParams = "",
+ { mediaType = "application/json", ignoreErrors = false } = {}
+ ) {
+ if (queryParams && typeof queryParams != "string") {
+ queryParams = Object.keys(queryParams)
+ .map(
+ key =>
+ encodeURIComponent(key) + "=" + encodeURIComponent(queryParams[key])
+ )
+ .join("&");
}
if (queryParams) {
- url = url + "?" + queryParams
+ url = url + "?" + queryParams;
}
var headers = this.createHeaders(mediaType);
return this.processFetch(url, {
- method: 'get',
+ method: "get",
headers: headers,
credentials: "same-origin",
ignoreErrors
});
},
- delete: function (url, { ignoreErrors = false } = {}) {
+ delete: function(url, { ignoreErrors = false } = {}) {
var headers = this.createHeaders();
return this.processFetch(url, {
- method: 'delete',
+ method: "delete",
headers: headers,
credentials: "same-origin",
ignoreErrors
});
},
- processFetch: function (url, { method = 'get', headers, credentials = 'same-origin', body, ignoreErrors = false }) {
-
+ processFetch: function(
+ url,
+ {
+ method = "get",
+ headers,
+ credentials = "same-origin",
+ body,
+ ignoreErrors = false
+ }
+ ) {
const fetchConfig = {
method,
headers,
- credentials,
+ credentials
};
if (body) {
fetchConfig.body = body;
}
incrementCount();
- return fetch(url, fetchConfig).then((response) => {
- decrementCount();
- if (response.ok) {
- // No response body
- if (response.status === 204) {
- return Promise.resolve();
- } else {
- return Promise.resolve(response.json())
- }
- } else {
- return response.json().then(json => {
- const error = new Error(json.detail ? json.detail : response.statusText);
- error.details = this.createErrorDetails({ url, body, status: response.status, responseBody: json })
+ return fetch(url, fetchConfig)
+ .then(
+ response => {
+ decrementCount();
+ if (response.ok) {
+ // No response body
+ if (response.status === 204) {
+ return Promise.resolve();
+ } else {
+ return Promise.resolve(response.json());
+ }
+ } else {
+ return response.json().then(
+ json => {
+ // if json doesn't have detail key, stringify body
+ let errorMessage = json.detail;
+ if (!("detail" in json)) {
+ errorMessage = "Error: " + JSON.stringify(json);
+ }
+ const error = new Error(errorMessage);
+ error.details = this.createErrorDetails({
+ url,
+ body,
+ status: response.status,
+ responseBody: json
+ });
+ throw error;
+ },
+ e => {
+ // In case JSON parsing fails
+ const error = new Error(response.statusText);
+ error.details = this.createErrorDetails({
+ url,
+ body,
+ status: response.status
+ });
+ throw error;
+ }
+ );
+ }
+ },
+ error => {
+ decrementCount();
+ error.details = this.createErrorDetails({ url, body });
throw error;
- }, e => { // In case JSON parsing fails
- const error = new Error(response.statusText);
- error.details = this.createErrorDetails({ url, body, status: response.status })
- throw error;
- });
- }
- }, (error) => {
- decrementCount();
- error.details = this.createErrorDetails({ url, body });
- throw error;
- }).catch(error => {
-
- if (!ignoreErrors) {
- UnhandledErrorDispatcher.reportError({
- message: error.message,
- error: error,
- details: error.details,
- })
- }
- throw error;
- })
+ }
+ )
+ .catch(error => {
+ if (!ignoreErrors) {
+ UnhandledErrorDispatcher.reportError({
+ message: error.message,
+ error: error,
+ details: error.details
+ });
+ }
+ throw error;
+ });
},
- createErrorDetails: function ({ url, body, status = null, responseBody = null } = {}) {
+ createErrorDetails: function({
+ url,
+ body,
+ status = null,
+ responseBody = null
+ } = {}) {
return {
url,
body,
status,
response: responseBody
- }
+ };
}
-}
+};
diff --git a/django_airavata/static/common/js/errors/ValidationErrors.js b/django_airavata/static/common/js/errors/ValidationErrors.js
new file mode 100644
index 0000000..bf2e7b0
--- /dev/null
+++ b/django_airavata/static/common/js/errors/ValidationErrors.js
@@ -0,0 +1,27 @@
+export default {
+ createValidationFeedback(data, validationErrors) {
+ const validationFeedback = {};
+ if (!data) {
+ return validationFeedback;
+ }
+ for (const fieldName in data) {
+ if (data.hasOwnProperty(fieldName)) {
+ const errorMessages = validationErrors
+ ? validationErrors[fieldName]
+ : null;
+ if (errorMessages) {
+ validationFeedback[fieldName] = {
+ invalidFeedback: errorMessages,
+ state: "invalid"
+ };
+ } else {
+ validationFeedback[fieldName] = {
+ invalidFeedback: null,
+ state: null
+ };
+ }
+ }
+ }
+ return validationFeedback;
+ }
+};
diff --git a/django_airavata/static/common/js/index.js b/django_airavata/static/common/js/index.js
index c39dbd6..8b7ce4b 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -10,6 +10,7 @@ import ShareButton from "./components/ShareButton.vue";
import UnsavedChangesGuard from "./components/UnsavedChangesGuard.vue";
import GlobalErrorHandler from "./errors/GlobalErrorHandler";
+import ValidationErrors from "./errors/ValidationErrors";
import ListLayout from "./layouts/ListLayout.vue";
@@ -32,7 +33,8 @@ exports.components = {
};
exports.errors = {
- GlobalErrorHandler
+ GlobalErrorHandler,
+ ValidationErrors
};
exports.layouts = {