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/08/17 19:35:57 UTC
[airavata-django-portal] 01/05: AIRAVATA-2727 AIRAVATA-2638 Global
error handling
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
commit 7545f270dd311b80a3d8ac8b1439e0b95054e4fa
Author: Marcus Christie <ma...@iu.edu>
AuthorDate: Fri Aug 17 14:29:16 2018 -0400
AIRAVATA-2727 AIRAVATA-2638 Global error handling
---
.../admin/static/django_airavata_admin/src/main.js | 10 +-
django_airavata/apps/api/exceptions.py | 30 ++
django_airavata/apps/api/serializers.py | 12 +-
.../django_airavata_api/js/errors/ErrorReporter.js | 9 +
.../js/errors/GlobalErrorHandler.js | 27 ++
.../js/errors/UnhandledError.js | 19 +
.../js/errors/UnhandledErrorDispatcher.js | 18 +
.../js/errors/UnhandledErrorDisplayList.js | 21 +
.../api/static/django_airavata_api/js/index.js | 12 +-
.../js/services/ServiceFactory.js | 12 +-
.../django_airavata_api/js/utils/FetchUtils.js | 133 ++++---
django_airavata/apps/api/thrift_utils.py | 8 +-
django_airavata/apps/api/views.py | 4 +-
django_airavata/settings.py | 2 +
.../common/js/components/NotificationsDisplay.vue | 75 ++++
django_airavata/static/common/js/index.js | 2 +
.../static/common/js/notifications/Notification.js | 14 +
.../common/js/notifications/NotificationList.js | 31 ++
django_airavata/static/common/package-lock.json | 424 +++++----------------
19 files changed, 460 insertions(+), 403 deletions(-)
diff --git a/django_airavata/apps/admin/static/django_airavata_admin/src/main.js b/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
index fa8a27b..46e5ecc 100644
--- a/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
+++ b/django_airavata/apps/admin/static/django_airavata_admin/src/main.js
@@ -11,9 +11,14 @@ import ComputeResourcePreferenceDashboard from './components/dashboards/ComputeR
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap-vue/dist/bootstrap-vue.css'
+import { components } from 'django-airavata-common-ui'
+import { errors } from 'django-airavata-api'
+
import router from './router';
import store from './store/store';
+errors.GlobalErrorHandler.init();
+
Vue.config.productionTip = false;
Vue.use(BootstrapVue)
@@ -23,7 +28,7 @@ Vue.use(VueRouter);
export function initializeApacheAiravataDashboard(dashboardName) {
var template = `
- <div class="vmain"><Loading/>
+ <div class="vmain"><notifications-display/><Loading/>
<transition name="fade">
<router-view>
</router-view>
@@ -40,7 +45,8 @@ export function initializeApacheAiravataDashboard(dashboardName) {
CredentialStore,
Loading,
ComputeResourceDashboard,
- ComputeResourcePreferenceDashboard
+ ComputeResourcePreferenceDashboard,
+ 'notifications-display': components.NotificationsDisplay,
},
mounted:function () {
if (this.$router.currentRoute.path === '/') {
diff --git a/django_airavata/apps/api/exceptions.py b/django_airavata/apps/api/exceptions.py
new file mode 100644
index 0000000..4075189
--- /dev/null
+++ b/django_airavata/apps/api/exceptions.py
@@ -0,0 +1,30 @@
+import json
+import logging
+
+from rest_framework import serializers, status
+from rest_framework.response import Response
+from rest_framework.views import exception_handler
+
+from airavata.api.error.ttypes import AiravataSystemException
+
+log = logging.getLogger(__name__)
+
+
+def custom_exception_handler(exc, context):
+ # Call REST framework's default exception handler first,
+ # to get the standard error response.
+ response = exception_handler(exc, context)
+
+ if isinstance(exc, AiravataSystemException):
+ log.error("AiravataSystemException", exc_info=exc)
+ return Response(
+ {'detail': str(exc)},
+ 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))
+
+ log.debug("API exception", exc_info=exc)
+ return response
diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index a174565..585f9c6 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -44,7 +44,14 @@ class FullyEncodedHyperlinkedIdentityField(serializers.HyperlinkedIdentityField)
lookup_value = getattr(obj, self.lookup_field)
else:
lookup_value = obj.get(self.lookup_field)
- encoded_lookup_value = quote(lookup_value, safe="")
+ try:
+ encoded_lookup_value = quote(lookup_value, safe="")
+ except Exception as e:
+ log.warning(
+ "Failed to encode lookup_value [{}] for lookup_field "
+ "[{}] of object [{}]".format(
+ lookup_value, self.lookup_field, obj))
+ raise
# Bit of a hack. Django's URL reversing does URL encoding but it doesn't
# encode all characters including some like '/' that are used in URL
# mappings.
@@ -451,6 +458,9 @@ class GroupResourceProfileSerializer(
updatedTime = UTCPosixTimestampDateTimeField(allow_null=True)
userHasWriteAccess = serializers.SerializerMethodField()
+ class Meta:
+ required = ('groupResourceProfileName',)
+
def update(self, instance, validated_data):
result = super().update(instance, validated_data)
result._removed_compute_resource_preferences = []
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorReporter.js b/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorReporter.js
new file mode 100644
index 0000000..7dc83e2
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/ErrorReporter.js
@@ -0,0 +1,9 @@
+
+class ErrorReporter {
+ reportUnhandledError(unhandledError) {
+ // TODO: send to the server so it can be logged there
+ console.log(JSON.stringify(unhandledError, null, 4));
+ }
+}
+
+export default new ErrorReporter();
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/errors/GlobalErrorHandler.js b/django_airavata/apps/api/static/django_airavata_api/js/errors/GlobalErrorHandler.js
new file mode 100644
index 0000000..02b5278
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/GlobalErrorHandler.js
@@ -0,0 +1,27 @@
+// import StackTrace from 'stacktrace-js'
+
+import UnhandledErrorDispatcher from './UnhandledErrorDispatcher'
+
+class GlobalErrorHandler {
+
+ init() {
+ console.log("Initializing GlobalErrorHandler...");
+ window.onerror = this.handleGlobalError;
+ }
+
+ handleGlobalError(msg, url, lineNo, columnNo, error) {
+ UnhandledErrorDispatcher.reportError({
+ message: msg,
+ error: error,
+ details: {
+ url,
+ lineNo,
+ columnNo
+ }
+ });
+
+ return false;
+ }
+}
+
+export default new GlobalErrorHandler()
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledError.js b/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledError.js
new file mode 100644
index 0000000..33a80ae
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledError.js
@@ -0,0 +1,19 @@
+
+let idSequence = 0;
+class UnhandledError {
+ constructor({message = null, error = null, details = null, suppressDisplay = false, suppressLogging = false}) {
+ this.id = idSequence++;
+ this.message = message;
+ this.error = error;
+ this.details = details;
+ this.suppressDisplay = suppressDisplay;
+ this.suppressLogging = suppressLogging;
+ this.createdDate = new Date();
+ }
+
+ get displayMessage() {
+ return this.error && this.error.message ? this.error.message : this.message;
+ }
+}
+
+export default UnhandledError;
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledErrorDispatcher.js b/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledErrorDispatcher.js
new file mode 100644
index 0000000..1a8cee5
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledErrorDispatcher.js
@@ -0,0 +1,18 @@
+import ErrorReporter from './ErrorReporter'
+import UnhandledError from './UnhandledError'
+import UnhandledErrorList from './UnhandledErrorDisplayList';
+
+class UnhandledErrorDispatcher {
+
+ reportError({ message = null, error = null, details = null, suppressDisplay = false, suppressLogging = false }) {
+ const unhandledError = new UnhandledError({message, error, details, suppressDisplay, suppressLogging});
+ if (!unhandledError.suppressDisplay) {
+ UnhandledErrorList.add(unhandledError);
+ }
+ if (!unhandledError.suppressLogging) {
+ ErrorReporter.reportUnhandledError(unhandledError);
+ }
+ }
+}
+
+export default new UnhandledErrorDispatcher();
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledErrorDisplayList.js b/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledErrorDisplayList.js
new file mode 100644
index 0000000..411beb9
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/errors/UnhandledErrorDisplayList.js
@@ -0,0 +1,21 @@
+
+class UnhandledErrorDisplayList {
+ constructor() {
+ this.unhandledErrors = [];
+ }
+
+ add(unhandledError) {
+ this.unhandledErrors.push(unhandledError);
+ }
+
+ remove(unhandledError) {
+ const i = this.unhandledErrors.indexOf(unhandledError);
+ this.unhandledErrors.splice(i, 1);
+ }
+
+ get list() {
+ return this.unhandledErrors;
+ }
+}
+
+export default new UnhandledErrorDisplayList();
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 0e3502b..ba494bc 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,3 +1,6 @@
+import GlobalErrorHandler from './errors/GlobalErrorHandler'
+import UnhandledError from './errors/UnhandledError'
+import UnhandledErrorDisplayList from './errors/UnhandledErrorDisplayList'
import ApplicationInterfaceDefinition from './models/ApplicationInterfaceDefinition'
import ApplicationModule from './models/ApplicationModule'
@@ -28,7 +31,6 @@ import ExperimentSearchService from './services/ExperimentSearchService'
import FullExperimentService from './services/FullExperimentService'
import ProjectService from './services/ProjectService'
import GroupService from './services/GroupService'
-import GroupResourceProfileService from './services/GroupResourceProfileService'
import UserProfileService from './services/UserProfileService'
import CloudJobSubmissionService from './services/CloudJobSubmissionService'
import GlobusJobSubmissionService from './services/GlobusJobSubmissionService'
@@ -42,6 +44,12 @@ import ServiceFactory from './services/ServiceFactory'
import FetchUtils from './utils/FetchUtils'
import PaginationIterator from './utils/PaginationIterator'
+exports.errors = {
+ GlobalErrorHandler,
+ UnhandledError,
+ UnhandledErrorDisplayList,
+}
+
exports.models = {
ApplicationInterfaceDefinition,
ApplicationModule,
@@ -86,7 +94,7 @@ exports.services = {
GridFTPDataMovementService,
SCPDataMovementService,
UnicoreDataMovementService,
- ServiceFactory
+ ServiceFactory,
}
exports.utils = {
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/services/ServiceFactory.js b/django_airavata/apps/api/static/django_airavata_api/js/services/ServiceFactory.js
index 6ab85b5..7d9d408 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/services/ServiceFactory.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/services/ServiceFactory.js
@@ -159,7 +159,7 @@ class ServiceFactory {
}
let pathParamsMapping = parsePathParams(config.url);
let queryParamsMapping = parseQueryMapping(config.queryParams);
- serviceObj[functionName] = function (params = {}) {
+ serviceObj[functionName] = function (params = {}, { ignoreErrors } = { ignoreErrors: false }) {
let url = config.url;
let paramKeys = Object.keys(params);
let queryParams = {};
@@ -198,13 +198,13 @@ class ServiceFactory {
}
switch (config.requestType.toLowerCase()) {
case postKey:
- return FetchUtils.post(url, bodyParams, queryParams).then(resultHandler);
+ return FetchUtils.post(url, bodyParams, queryParams, { ignoreErrors }).then(resultHandler);
case getKey:
- return FetchUtils.get(url, queryParams).then(paginationHandler);
+ return FetchUtils.get(url, queryParams, { ignoreErrors }).then(paginationHandler);
case putKey:
- return FetchUtils.put(url, bodyParams).then(resultHandler);
+ return FetchUtils.put(url, bodyParams, { ignoreErrors }).then(resultHandler);
case delKey:
- return FetchUtils.delete(url);
+ return FetchUtils.delete(url, { ignoreErrors });
}
}
}
@@ -212,4 +212,4 @@ class ServiceFactory {
}
}
-export default new ServiceFactory(serviceConfiguration);
\ No newline at end of file
+export default new ServiceFactory(serviceConfiguration);
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 7395690..f16d382 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,3 +1,5 @@
+import UnhandledErrorDispatcher from "../errors/UnhandledErrorDispatcher";
+
var count = 0;
const parseQueryParams = function (url, queryParams = "") {
if (queryParams && typeof(queryParams) != "string") {
@@ -56,61 +58,31 @@ export default {
}
return headers;
},
- post: function (url, body, queryParams = "", mediaType = "application/json") {
+ 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");
}
- console.log("post body", body);
url = parseQueryParams(url, queryParams);
- incrementCount();
- return fetch(url, {
+ return this.processFetch(url, {
method: 'post',
body: (body instanceof FormData || typeof body === 'string') ? body : JSON.stringify(body),
headers: headers,
- credentials: "same-origin"
- }).then((response) => {
- decrementCount();
- if (response.ok) {
- return Promise.resolve(response.json())
- } else {
- let error = new Error(response.statusText);
- return response.json().then(json => {
- error.data = json;
- })
- .then(() => Promise.reject(error), () => Promise.reject(error));
- }
- }, (response) => {
- decrementCount();
- return Promise.reject(response);
- })
+ credentials: "same-origin",
+ ignoreErrors
+ });
},
- put: function (url, body, mediaType = "application/json") {
+ put: function (url, body, { mediaType = "application/json", ignoreErrors = false } = {}) {
var headers = this.createHeaders(mediaType);
- incrementCount();
- return fetch(url, {
+ return this.processFetch(url, {
method: 'put',
body: (body instanceof FormData || typeof body === 'string') ? body : JSON.stringify(body),
headers: headers,
credentials: "same-origin"
- }).then((response) => {
- decrementCount();
- if (response.ok) {
- return Promise.resolve(response.json())
- } else {
- let error = new Error(response.statusText);
- return response.json().then(json => {
- error.data = json;
- })
- .then(() => Promise.reject(error), () => Promise.reject(error));
- }
- }, (response) => {
- decrementCount();
- return Promise.reject(response);
- })
+ });
},
- get: function (url, queryParams = "", mediaType = "application/json") {
+ 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("&")
}
@@ -118,46 +90,73 @@ export default {
url = url + "?" + queryParams
}
var headers = this.createHeaders(mediaType);
- incrementCount();
- return fetch(url, {
+ return this.processFetch(url, {
method: 'get',
headers: headers,
credentials: "same-origin"
- }).then((response) => {
- decrementCount();
- if (response.ok) {
- return Promise.resolve(response.json())
- } else {
- let error = new Error(response.statusText);
- return response.json().then(json => {
- error.data = json;
- })
- .then(() => Promise.reject(error), () => Promise.reject(error));
- }
- }, (response) => {
- decrementCount();
- return Promise.reject(response);
- })
+ });
},
- delete: function (url) {
+ delete: function (url, { ignoreErrors = false } = {}) {
var headers = this.createHeaders();
- incrementCount();
- return fetch(url, {
+ return this.processFetch(url, {
method: 'delete',
headers: headers,
credentials: "same-origin"
- }).then((response) => {
+ });
+ },
+ processFetch: function(url, {method = 'get', headers, credentials = 'same-origin', body, ignoreErrors = false}) {
+
+ const fetchConfig = {
+ method,
+ headers,
+ credentials,
+ };
+ if (body) {
+ fetchConfig.body = body;
+ }
+ incrementCount();
+ return fetch(url, fetchConfig).then((response) => {
decrementCount();
- // Not expecting a response body
- if (response.ok && response.status === 204) {
- return Promise.resolve();
+ if (response.ok) {
+ // No response body
+ if (response.status === 204) {
+ return Promise.resolve();
+ } else {
+ return Promise.resolve(response.json())
+ }
} else {
- let error = new Error(response.statusText);
return response.json().then(json => {
- error.data = json;
+ const error = new Error(json.detail ? json.detail : response.statusText);
+ 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;
+ }).catch(error => {
+
+ if (!ignoreErrors) {
+ UnhandledErrorDispatcher.reportError({
+ message: error.message,
+ error: error,
+ details: error.details,
})
- .then(() => Promise.reject(error), () => Promise.reject(error));
}
+ throw error;
})
+ },
+ createErrorDetails: function({url, body, status = null, responseBody = null}={}) {
+ return {
+ url,
+ body,
+ status,
+ response: responseBody
+ }
}
-}
\ No newline at end of file
+}
diff --git a/django_airavata/apps/api/thrift_utils.py b/django_airavata/apps/api/thrift_utils.py
index bbff521..be59ab9 100644
--- a/django_airavata/apps/api/thrift_utils.py
+++ b/django_airavata/apps/api/thrift_utils.py
@@ -90,8 +90,12 @@ def create_serializer_class(thrift_data_type, enable_date_time_conversion=False)
for field in thrift_spec:
# Don't replace existing attrs to allow subclasses to override
if field and field[2] not in attrs:
- required = field[2] in meta.required if meta else False
- read_only = field[2] in meta.read_only if meta else False
+ required = (field[2] in meta.required
+ if meta and hasattr(meta, 'required')
+ else False)
+ read_only = (field[2] in meta.read_only
+ if meta and hasattr(meta, 'read_only')
+ else False)
allow_null = not required
field_serializer = process_field(
field, enable_date_time_conversion, required=required, read_only=read_only,
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index f3b4359..2edcae0 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -693,9 +693,7 @@ class GroupResourceProfileViewSet(APIBackedViewSet):
group_resource_profile.gatewayId = self.gateway_id
group_resource_profile_id = self.request.airavata_client.createGroupResourceProfile(authzToken=self.authz_token,
groupResourceProfile=group_resource_profile)
- group_resource_profile = self.request.airavata_client.getGroupResourceProfile(
- self.authz_token, group_resource_profile_id)
- serializer.instance = group_resource_profile
+ group_resource_profile.groupResourceProfileId = group_resource_profile_id
def perform_update(self, serializer):
grp = serializer.save()
diff --git a/django_airavata/settings.py b/django_airavata/settings.py
index 2748bc7..ed05acf 100644
--- a/django_airavata/settings.py
+++ b/django_airavata/settings.py
@@ -174,6 +174,8 @@ REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
+ 'EXCEPTION_HANDLER':
+ 'django_airavata.apps.api.exceptions.custom_exception_handler',
}
AUTHENTICATION_BACKENDS = [
diff --git a/django_airavata/static/common/js/components/NotificationsDisplay.vue b/django_airavata/static/common/js/components/NotificationsDisplay.vue
new file mode 100644
index 0000000..6a71aac
--- /dev/null
+++ b/django_airavata/static/common/js/components/NotificationsDisplay.vue
@@ -0,0 +1,75 @@
+<template>
+ <div id="notifications-display">
+ <transition-group name="fade" tag="div">
+ <b-alert v-for="error in errors"
+ :variant="variant(error)" :key="error.id"
+ show dismissible @dismissed="dismissedError(error)">
+ {{ error.message }}
+ </b-alert>
+ <b-alert v-for="notification in notifications"
+ :variant="variant(notification)" :key="notification.id"
+ show dismissible @dismissed="dismissedNotification(notification)">
+ {{ notification.message }}
+ </b-alert>
+ </transition-group>
+ </div>
+</template>
+
+<script>
+
+import { errors } from 'django-airavata-api'
+import Notification from '../notifications/Notification'
+import NotificationList from '../notifications/NotificationList'
+
+export default {
+ name: "notifications-display",
+ data () {
+ return {
+ notifications: NotificationList.list,
+ unhandledErrors: errors.UnhandledErrorDisplayList.list,
+ }
+ },
+ computed: {
+ errors: function() {
+
+ return this.unhandledErrors.map(unhandledError => {
+ return new Notification("UNHANDLED-ERROR-" + unhandledError.id, {
+ type: "ERROR",
+ message: unhandledError.displayMessage,
+ details: unhandledError,
+ createdDate: unhandledError.createdDate,
+ });
+ })
+ }
+ },
+ methods: {
+ dismissedNotification: function(notification) {
+ NotificationList.remove(notification);
+ },
+ dismissedError: function(error) {
+ errors.UnhandledErrorList.remove(error.details);
+ },
+ variant: function(notification) {
+ if (notification.type === "SUCCESS") {
+ return "success";
+ } else if (notification.type === "ERROR") {
+ return "danger";
+ } else {
+ return "secondary";
+ }
+ }
+ },
+}
+</script>
+
+<style>
+#notifications-display {
+ position: fixed;
+ top: 75px;
+ left: 20vw;
+ width: 60vw;
+ z-index: 10000;
+}
+</style>
+
+
diff --git a/django_airavata/static/common/js/index.js b/django_airavata/static/common/js/index.js
index 9c9c22f..28a2cfa 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -1,6 +1,7 @@
import ApplicationCard from './components/ApplicationCard.vue'
import Autocomplete from './components/Autocomplete.vue'
import AutocompleteTextInput from './components/AutocompleteTextInput.vue'
+import NotificationsDisplay from './components/NotificationsDisplay.vue'
import Pager from './components/Pager.vue'
import ShareButton from './components/ShareButton.vue'
@@ -13,6 +14,7 @@ exports.components = {
ApplicationCard,
Autocomplete,
AutocompleteTextInput,
+ NotificationsDisplay,
ShareButton,
}
diff --git a/django_airavata/static/common/js/notifications/Notification.js b/django_airavata/static/common/js/notifications/Notification.js
new file mode 100644
index 0000000..1b8864e
--- /dev/null
+++ b/django_airavata/static/common/js/notifications/Notification.js
@@ -0,0 +1,14 @@
+
+class Notification {
+ constructor(id, { type = "SUCCESS", message = null, details = null, dismissable = true, duration = 0, createdDate = null }) {
+ this.id = id;
+ this.type = type;
+ this.message = message;
+ this.details = details;
+ this.dismissable = dismissable;
+ this.duration = duration;
+ this.createdDate = createdDate ? createdDate : new Date();
+ }
+}
+
+export default Notification;
diff --git a/django_airavata/static/common/js/notifications/NotificationList.js b/django_airavata/static/common/js/notifications/NotificationList.js
new file mode 100644
index 0000000..09457a7
--- /dev/null
+++ b/django_airavata/static/common/js/notifications/NotificationList.js
@@ -0,0 +1,31 @@
+import { errors } from 'django-airavata-api'
+
+import Notification from './Notification'
+
+let notificationIdSequence = 0;
+
+class NotificationList {
+
+ constructor() {
+ this.notifications = [];
+ }
+
+ add(notification) {
+ this.notifications.push(notification);
+ }
+
+ remove(notification) {
+ const i = this.notifications.indexOf(notification);
+ this.notifications.splice(i, 1);
+ }
+
+ get list() {
+ return this.notifications;
+ }
+
+ getNextId() {
+ return "NOTIFICATION-" + notificationIdSequence++;
+ }
+}
+
+export default new NotificationList();
diff --git a/django_airavata/static/common/package-lock.json b/django_airavata/static/common/package-lock.json
index 564d5ff..664d58a 100644
--- a/django_airavata/static/common/package-lock.json
+++ b/django_airavata/static/common/package-lock.json
@@ -213,10 +213,13 @@
"dev": true
},
"asn1": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
- "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=",
- "dev": true
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
},
"asn1.js": {
"version": "4.10.1",
@@ -256,9 +259,9 @@
}
},
"assert-plus": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
- "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
"assign-symbols": {
@@ -327,15 +330,15 @@
}
},
"aws-sign2": {
- "version": "0.6.0",
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
- "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
"dev": true
},
"aws4": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
- "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==",
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
"dev": true
},
"babel-code-frame": {
@@ -1140,15 +1143,6 @@
"multicast-dns-service-types": "^1.1.0"
}
},
- "boom": {
- "version": "2.10.1",
- "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
- "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
- "dev": true,
- "requires": {
- "hoek": "2.x.x"
- }
- },
"bootstrap": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.1.3.tgz",
@@ -1390,15 +1384,15 @@
}
},
"caniuse-db": {
- "version": "1.0.30000872",
- "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000872.tgz",
- "integrity": "sha1-P25Ttj03N2i/meiWEz1m74nEmZk=",
+ "version": "1.0.30000877",
+ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000877.tgz",
+ "integrity": "sha512-9RcqvE8HYgdZZzFW6xBmj/CeCaTyCJdUhgkueBCq47AK//w/Yzlg0zcfV1GTlh3jyYEbresGfY2vDEG/AaK/dQ==",
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30000865",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz",
- "integrity": "sha512-vs79o1mOSKRGv/1pSkp4EXgl4ZviWeYReXw60XfacPU64uQWZwJT6vZNmxRF9O+6zu71sJwMxLK5JXxbzuVrLw==",
+ "version": "1.0.30000877",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000877.tgz",
+ "integrity": "sha512-h04kV/lcuhItU1CZTJOxUEk/9R+1XeJqgc67E+XC8J9TjPM8kzVgOn27ZtRdDUo8O5F8U4QRCzDWJrVym3w3Cg==",
"dev": true
},
"caseless": {
@@ -1654,9 +1648,9 @@
}
},
"commander": {
- "version": "2.16.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.16.0.tgz",
- "integrity": "sha512-sVXqklSaotK9at437sFlFpyOcJonxe0yST/AG9DkQKUdIE6IqGIMv4SfAQSKaJbSdVEJYItASCrBiVQHq1HQew==",
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"dev": true
},
"commondir": {
@@ -1867,15 +1861,6 @@
"which": "^1.2.9"
}
},
- "cryptiles": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
- "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
- "dev": true,
- "requires": {
- "boom": "2.x.x"
- }
- },
"crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
@@ -2040,14 +2025,6 @@
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- }
}
},
"date-now": {
@@ -2090,13 +2067,12 @@
"dev": true
},
"define-properties": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
- "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
- "foreach": "^2.0.5",
- "object-keys": "^1.0.8"
+ "object-keys": "^1.0.12"
}
},
"define-property": {
@@ -4044,15 +4020,15 @@
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.55",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.55.tgz",
- "integrity": "sha1-8VDhCyC3fZ1Br8yjEu/gw7Gn/c4=",
+ "version": "1.3.58",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.58.tgz",
+ "integrity": "sha512-AGJxlBEn2wOohxqWZkISVsOjZueKTQljfEODTDSEiMqSpH0S+xzV+/5oEM9AGaqhu7DzrpKOgU7ocQRjj0nJmg==",
"dev": true
},
"elliptic": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
- "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
+ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@@ -4139,9 +4115,9 @@
}
},
"es5-ext": {
- "version": "0.10.45",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz",
- "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==",
+ "version": "0.10.46",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz",
+ "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==",
"dev": true,
"requires": {
"es6-iterator": "~2.0.3",
@@ -4714,9 +4690,9 @@
"dev": true
},
"follow-redirects": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.2.tgz",
- "integrity": "sha512-kssLorP/9acIdpQ2udQVTiCS5LQmdEz9mvdIfDcl1gYX2tPKFADHSyFdvJS040XdFsPzemWtgI3q8mFVCxtX8A==",
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.5.tgz",
+ "integrity": "sha512-GHjtHDlY/ehslqv0Gr5N0PUJppgg/q0rOBvX0na1s7y1A3LWxPqCYU76s3Z1bM4+UZB4QF0usaXLT5wFpof5PA==",
"dev": true,
"requires": {
"debug": "^3.1.0"
@@ -4748,12 +4724,6 @@
"for-in": "^1.0.1"
}
},
- "foreach": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
- "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
- "dev": true
- },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -4761,13 +4731,13 @@
"dev": true
},
"form-data": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
- "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
+ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
- "combined-stream": "^1.0.5",
+ "combined-stream": "1.0.6",
"mime-types": "^2.1.12"
}
},
@@ -5423,14 +5393,6 @@
"dev": true,
"requires": {
"assert-plus": "^1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- }
}
},
"glob": {
@@ -5555,31 +5517,19 @@
"dev": true
},
"har-schema": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
- "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
"dev": true
},
"har-validator": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
- "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"dev": true,
"requires": {
- "ajv": "^4.9.1",
- "har-schema": "^1.0.5"
- },
- "dependencies": {
- "ajv": {
- "version": "4.11.8",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
- "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
- "dev": true,
- "requires": {
- "co": "^4.6.0",
- "json-stable-stringify": "^1.0.1"
- }
- }
+ "ajv": "^5.1.0",
+ "har-schema": "^2.0.0"
}
},
"has": {
@@ -5669,18 +5619,6 @@
"minimalistic-assert": "^1.0.1"
}
},
- "hawk": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
- "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
- "dev": true,
- "requires": {
- "boom": "2.x.x",
- "cryptiles": "2.x.x",
- "hoek": "2.x.x",
- "sntp": "1.x.x"
- }
- },
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
@@ -5698,12 +5636,6 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
- "hoek": {
- "version": "2.16.3",
- "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
- "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
- "dev": true
- },
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -5902,12 +5834,12 @@
}
},
"http-signature": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
- "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"dev": true,
"requires": {
- "assert-plus": "^0.2.0",
+ "assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
@@ -6440,15 +6372,6 @@
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
"dev": true
},
- "json-stable-stringify": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
- "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
- "dev": true,
- "requires": {
- "jsonify": "~0.0.0"
- }
- },
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -6467,12 +6390,6 @@
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
"dev": true
},
- "jsonify": {
- "version": "0.0.0",
- "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
- "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
- "dev": true
- },
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
@@ -6483,14 +6400,6 @@
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- }
}
},
"killable": {
@@ -7009,9 +6918,9 @@
"dev": true
},
"neo-async": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz",
- "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==",
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz",
+ "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==",
"dev": true
},
"next-tick": {
@@ -7036,9 +6945,9 @@
"dev": true
},
"node-gyp": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.7.0.tgz",
- "integrity": "sha512-qDQE/Ft9xXP6zphwx4sD0t+VhwV7yFaloMpfbL2QnnDZcyaiakWlLdtFGGQfTAwpFHdpbRhRxVhIHN1OKAjgbg==",
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
+ "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
"dev": true,
"requires": {
"fstream": "^1.0.0",
@@ -7048,43 +6957,13 @@
"nopt": "2 || 3",
"npmlog": "0 || 1 || 2 || 3 || 4",
"osenv": "0",
- "request": ">=2.9.0 <2.82.0",
+ "request": "^2.87.0",
"rimraf": "2",
"semver": "~5.3.0",
"tar": "^2.0.0",
"which": "1"
},
"dependencies": {
- "request": {
- "version": "2.81.0",
- "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
- "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
- "dev": true,
- "requires": {
- "aws-sign2": "~0.6.0",
- "aws4": "^1.2.1",
- "caseless": "~0.12.0",
- "combined-stream": "~1.0.5",
- "extend": "~3.0.0",
- "forever-agent": "~0.6.1",
- "form-data": "~2.1.1",
- "har-validator": "~4.2.1",
- "hawk": "~3.1.3",
- "http-signature": "~1.1.0",
- "is-typedarray": "~1.0.0",
- "isstream": "~0.1.2",
- "json-stringify-safe": "~5.0.1",
- "mime-types": "~2.1.7",
- "oauth-sign": "~0.8.1",
- "performance-now": "^0.2.0",
- "qs": "~6.4.0",
- "safe-buffer": "^5.0.1",
- "stringstream": "~0.0.4",
- "tough-cookie": "~2.3.0",
- "tunnel-agent": "^0.6.0",
- "uuid": "^3.0.0"
- }
- },
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
@@ -7125,9 +7004,9 @@
}
},
"node-sass": {
- "version": "4.9.2",
- "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.2.tgz",
- "integrity": "sha512-LdxoJLZutx0aQXHtWIYwJKMj+9pTjneTcLWJgzf2XbGu0q5pRNqW5QvFCEdm3mc5rJOdru/mzln5d0EZLacf6g==",
+ "version": "4.9.3",
+ "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.3.tgz",
+ "integrity": "sha512-XzXyGjO+84wxyH7fV6IwBOTrEBe2f0a6SBze9QWWYR/cL74AcQUks2AsqcCZenl/Fp/JVbuEaLpgrLtocwBUww==",
"dev": true,
"requires": {
"async-foreach": "^0.1.3",
@@ -7143,7 +7022,7 @@
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
"nan": "^2.10.0",
- "node-gyp": "^3.3.1",
+ "node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "2.87.0",
"sass-graph": "^2.2.4",
@@ -7392,12 +7271,12 @@
}
},
"original": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/original/-/original-1.0.1.tgz",
- "integrity": "sha512-IEvtB5vM5ULvwnqMxWBLxkS13JIEXbakizMSo3yoPNPCIWzg8TG3Usn/UhXoZFM/m+FuEA20KdzPSFq/0rS+UA==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
"dev": true,
"requires": {
- "url-parse": "~1.4.0"
+ "url-parse": "^1.4.3"
}
},
"os-browserify": {
@@ -7578,9 +7457,9 @@
"dev": true
},
"path-parse": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
- "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
"dev": true
},
"path-to-regexp": {
@@ -7622,9 +7501,9 @@
}
},
"performance-now": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
- "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true
},
"pify": {
@@ -7656,14 +7535,14 @@
}
},
"popper.js": {
- "version": "1.14.3",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.3.tgz",
- "integrity": "sha1-FDj5jQRqz3tNeM1QK/QYrGTU8JU="
+ "version": "1.14.4",
+ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.4.tgz",
+ "integrity": "sha1-juwdj/AqWjoVLdQ0FKFce3n9abY="
},
"portfinder": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz",
- "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=",
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.16.tgz",
+ "integrity": "sha512-icBXCFQxzlK2PMepOM0QeEdPPFSLAaXXeuKOv5AClJlMy1oVCBrkDGJ12IZYesI/BF8mpeVco3vRCmgeBb4+hw==",
"dev": true,
"requires": {
"async": "^1.5.2",
@@ -8418,9 +8297,9 @@
"dev": true
},
"qs": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
- "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
},
"query-string": {
@@ -8452,9 +8331,9 @@
"dev": true
},
"randomatic": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.0.0.tgz",
- "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.0.tgz",
+ "integrity": "sha512-KnGPVE0lo2WoXxIZ7cPR8YBpiol4gsSuOwDSg410oHh80ZMp5EiypNqL2K4Z77vJn6lB5rap7IkAmcUlalcnBQ==",
"dev": true,
"requires": {
"is-number": "^4.0.0",
@@ -8782,70 +8661,6 @@
"tough-cookie": "~2.3.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.1.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- },
- "aws-sign2": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
- "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
- "dev": true
- },
- "form-data": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
- "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
- "dev": true,
- "requires": {
- "asynckit": "^0.4.0",
- "combined-stream": "1.0.6",
- "mime-types": "^2.1.12"
- }
- },
- "har-schema": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
- "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
- "dev": true
- },
- "har-validator": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
- "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
- "dev": true,
- "requires": {
- "ajv": "^5.1.0",
- "har-schema": "^2.0.0"
- }
- },
- "http-signature": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
- "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
- "dev": true,
- "requires": {
- "assert-plus": "^1.0.0",
- "jsprim": "^1.2.2",
- "sshpk": "^1.7.0"
- }
- },
- "performance-now": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
- "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
- "dev": true
- },
- "qs": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
- "dev": true
- }
}
},
"require-directory": {
@@ -9323,15 +9138,6 @@
}
}
},
- "sntp": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
- "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
- "dev": true,
- "requires": {
- "hoek": "2.x.x"
- }
- },
"sockjs": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
@@ -9507,14 +9313,6 @@
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- }
}
},
"static-extend": {
@@ -9615,12 +9413,6 @@
"safe-buffer": "~5.1.0"
}
},
- "stringstream": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
- "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==",
- "dev": true
- },
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@@ -9664,15 +9456,15 @@
},
"dependencies": {
"ajv": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
- "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+ "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.1"
+ "uri-js": "^4.2.2"
}
},
"fast-deep-equal": {
@@ -9688,9 +9480,9 @@
"dev": true
},
"schema-utils": {
- "version": "0.4.5",
- "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz",
- "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==",
+ "version": "0.4.7",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
+ "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
"dev": true,
"requires": {
"ajv": "^6.1.0",
@@ -10145,9 +9937,9 @@
"dev": true
},
"validate-npm-package-license": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
- "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
@@ -10175,14 +9967,6 @@
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
- "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
- "dev": true
- }
}
},
"vm-browserify": {
@@ -10308,15 +10092,15 @@
},
"dependencies": {
"ajv": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz",
- "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==",
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+ "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
"dev": true,
"requires": {
"fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
- "uri-js": "^4.2.1"
+ "uri-js": "^4.2.2"
}
},
"camelcase": {