Merge "Quick fix for Composer slowdown after a lot of typing - use “debounce” straw to collect key strokes - rift15919"
diff --git a/skyquake/plugins/accounts/src/account/account.jsx b/skyquake/plugins/accounts/src/account/account.jsx
index b7dbf35..4f011cb 100644
--- a/skyquake/plugins/accounts/src/account/account.jsx
+++ b/skyquake/plugins/accounts/src/account/account.jsx
@@ -106,8 +106,8 @@
self.props.router.push({pathname:'accounts'});
self.props.flux.actions.global.hideScreenLoader.defer();
},
- function() {
- self.props.flux.actions.global.showNotification("There was an error creating your account. Please contact your system administrator.");
+ function(error) {
+ self.props.flux.actions.global.showNotification(error);
self.props.flux.actions.global.hideScreenLoader.defer();
});
}
diff --git a/skyquake/plugins/accounts/src/account/accountSource.js b/skyquake/plugins/accounts/src/account/accountSource.js
index 52af857..45da0fb 100644
--- a/skyquake/plugins/accounts/src/account/accountSource.js
+++ b/skyquake/plugins/accounts/src/account/accountSource.js
@@ -50,6 +50,7 @@
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});;
});
},
@@ -133,7 +134,7 @@
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
- reject();
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});
});
@@ -177,7 +178,7 @@
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
- reject('error');
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});
});
@@ -204,7 +205,7 @@
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
Utils.checkAuthentication(xhr.status);
- reject('error');
+ reject(xhr.responseText || 'An error occurred. Check your logs for more information');
});
})
},
diff --git a/skyquake/plugins/composer/api/composer.js b/skyquake/plugins/composer/api/composer.js
index ebe5636..f9f0591 100644
--- a/skyquake/plugins/composer/api/composer.js
+++ b/skyquake/plugins/composer/api/composer.js
@@ -29,6 +29,7 @@
var Composer = {};
var FileManager = {};
+var PackageManager = {};
var DataCenters = {};
// Catalog module methods
Composer.get = function(req) {
@@ -303,7 +304,63 @@
});
}
-Composer.update = function(req) {
+PackageManager.upload = function(req) {
+ console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+ var api_server = req.query['api_server'];
+ // dev_download_server is for testing purposes.
+ // It is the direct IP address of the Node server where the
+ // package will be hosted.
+ var download_host = req.query['dev_download_server'];
+
+ if (!download_host) {
+ download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
+ }
+
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/package-create',
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true,
+ json: true,
+ body: {
+ input: {
+ 'external-url': download_host + '/composer/upload/' + req.file.filename,
+ 'package-type': 'VNFD',
+ 'package-id': uuid()
+ }
+ }
+ })
+ ]).then(function(result) {
+ var data = {};
+ data['transaction_id'] = result[0].body['output']['transaction-id'];
+
+ // Add a status checker on the transaction and then to delete the file later
+ PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id']);
+
+ // Return status to composer UI to update the status.
+ resolve({
+ statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
+ data: data
+ });
+ }).catch(function(error) {
+ var res = {};
+ console.log('Problem with PackageManager.upload', error);
+ res.statusCode = error.statusCode || 500;
+ res.errorMessage = {
+ error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
+ };
+ reject(res);
+ });
+ });
+};
+
+PackageManager.update = function(req) {
console.log(' Updating file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
// dev_download_server is for testing purposes.
@@ -359,22 +416,13 @@
});
};
-Composer.upload = function(req) {
- console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
+PackageManager.export = function(req) {
+ // /api/operations/package-export
var api_server = req.query['api_server'];
- // dev_download_server is for testing purposes.
- // It is the direct IP address of the Node server where the
- // package will be hosted.
- var download_host = req.query['dev_download_server'];
-
- if (!download_host) {
- download_host = req.protocol + '://' + req.get('host');//req.api_server + ':' + utils.getPortForProtocol(req.protocol);
- }
-
return new Promise(function(resolve, reject) {
Promise.all([
rp({
- uri: utils.confdPort(api_server) + '/api/operations/package-create',
+ uri: utils.confdPort(api_server) + '/api/operations/package-export',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
@@ -383,41 +431,94 @@
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
- body: {
- input: {
- 'external-url': download_host + '/composer/upload/' + req.file.filename,
- 'package-type': 'VNFD',
- 'package-id': uuid()
- }
- }
+ body: { "input": req.body}
})
]).then(function(result) {
var data = {};
- data['transaction_id'] = result[0].body['output']['transaction-id'];
-
- // Add a status checker on the transaction and then to delete the file later
- PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id']);
-
- // Return status to composer UI to update the status.
resolve({
statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
- data: data
+ data: result[0].body
});
}).catch(function(error) {
var res = {};
- console.log('Problem with Composer.upload', error);
+ console.log('Problem with PackageManager.export', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
- error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
+ error: error
};
reject(res);
});
});
-};
+}
+PackageManager.copy = function(req) {
+ // /api/operations/package-copy
+ var api_server = req.query['api_server'];
+ return new Promise(function(resolve, reject) {
+ Promise.all([
+ rp({
+ uri: utils.confdPort(api_server) + '/api/operations/package-copy',
+ method: 'POST',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ resolveWithFullResponse: true,
+ json: true,
+ body: { "input": req.body}
+ })
+ ]).then(function(result) {
+ var data = {};
+ resolve({
+ statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
+ data: result[0].body
+ });
+ }).catch(function(error) {
+ var res = {};
+ console.log('Problem with PackageManager.copy', error);
+ res.statusCode = error.statusCode || 500;
+ res.errorMessage = {
+ error: error
+ };
+ reject(res);
+ });
+ });
+}
+PackageManager.getJobStatus = function(req) {
+ var api_server = req.query["api_server"];
+ var uri = utils.confdPort(api_server);
+ var url = '/api/operational/copy-jobs';
+ var id = req.params['id'];
+ return new Promise(function(resolve, reject) {
+ request({
+ url: uri + url + '?deep',
+ method: 'GET',
+ headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
+ 'Authorization': req.get('Authorization')
+ }),
+ forever: constants.FOREVER_ON,
+ rejectUnauthorized: false,
+ }, function(error, response, body) {
+ if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
+ var data = JSON.parse(response.body)['rw-pkg-mgmt:copy-jobs'];
+ var returnData = [];
+ data && data.job.map(function(d) {
+ if(d['transaction-id'] == id) {
+ returnData.push(d)
+ }
+ })
+ resolve({
+ statusCode: response.statusCode,
+ data: returnData
+ })
+ };
+ })
+ })
+}
-Composer.addFile = function(req) {
+FileManager.addFile = function(req) {
console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
var download_host = req.query['dev_download_server'];
@@ -468,41 +569,6 @@
});
}
-Composer.exportPackage = function(req) {
- // /api/operations/package-export
- var api_server = req.query['api_server'];
- return new Promise(function(resolve, reject) {
- Promise.all([
- rp({
- uri: utils.confdPort(api_server) + '/api/operations/package-export',
- method: 'POST',
- headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
- 'Authorization': req.get('Authorization')
- }),
- forever: constants.FOREVER_ON,
- rejectUnauthorized: false,
- resolveWithFullResponse: true,
- json: true,
- body: { "input": req.body}
- })
- ]).then(function(result) {
- var data = {};
- resolve({
- statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
- data: result[0].body
- });
- }).catch(function(error) {
- var res = {};
- console.log('Problem with Composer.exportPackage', error);
- res.statusCode = error.statusCode || 500;
- res.errorMessage = {
- error: error
- };
- reject(res);
- });
- });
-}
-
FileManager.get = function(req) {
var api_server = req.query['api_server'];
var type = req.query['package_type'] && req.query['package_type'].toUpperCase();
@@ -654,5 +720,6 @@
}
module.exports = {
Composer:Composer,
- FileManager: FileManager
+ FileManager: FileManager,
+ PackageManager: PackageManager
};
diff --git a/skyquake/plugins/composer/routes.js b/skyquake/plugins/composer/routes.js
index 3782209..b3641aa 100644
--- a/skyquake/plugins/composer/routes.js
+++ b/skyquake/plugins/composer/routes.js
@@ -23,6 +23,7 @@
var C = require('./api/composer.js');
var Composer = C.Composer;
var FileManager = C.FileManager;
+var PackageManager = C.PackageManager;
var multer = require('multer');
var fs = require('fs');
var path = require('path');
@@ -103,28 +104,9 @@
res.send(error.errorMessage);
});
});
-router.post('/upload', cors(), upload.single('package'), function (req, res, next) {
- Composer.upload(req).then(function(data) {
- utils.sendSuccessResponse(data, res);
- }, function(error) {
- utils.sendErrorResponse(error, res);
- });
-});
-router.use('/upload', cors(), express.static('upload/packages'));
-
-router.post('/update', cors(), upload.single('package'), function (req, res, next) {
- Composer.update(req).then(function(data) {
- utils.sendSuccessResponse(data, res);
- }, function(error) {
- utils.sendErrorResponse(error, res);
- });
-});
-router.use('/update', cors(), express.static('upload/packages'));
-
-
router.post('/api/file-manager', cors(), upload.single('package'), function (req, res, next) {
- Composer.addFile(req).then(function(data) {
+ FileManager.addFile(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
@@ -153,8 +135,42 @@
});
});
+// Catalog operations via package manager
+
+router.post('/upload', cors(), upload.single('package'), function (req, res, next) {
+ PackageManager.upload(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.use('/upload', cors(), express.static('upload/packages'));
+
+router.post('/update', cors(), upload.single('package'), function (req, res, next) {
+ PackageManager.update(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.use('/update', cors(), express.static('upload/packages'));
+
router.post('/api/package-export', cors(), function (req, res, next) {
- Composer.exportPackage(req).then(function(data) {
+ PackageManager.export(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.post('/api/package-copy', cors(), function (req, res, next) {
+ PackageManager.copy(req).then(function(data) {
+ utils.sendSuccessResponse(data, res);
+ }, function(error) {
+ utils.sendErrorResponse(error, res);
+ });
+});
+router.get('/api/package-manager/jobs/:id', cors(), function (req, res, next) {
+ PackageManager.getJobStatus(req).then(function(data) {
utils.sendSuccessResponse(data, res);
}, function(error) {
utils.sendErrorResponse(error, res);
diff --git a/skyquake/plugins/composer/src/src/actions/CatalogPackageManagerActions.js b/skyquake/plugins/composer/src/src/actions/CatalogPackageManagerActions.js
index 22e32d3..2769c33 100644
--- a/skyquake/plugins/composer/src/src/actions/CatalogPackageManagerActions.js
+++ b/skyquake/plugins/composer/src/src/actions/CatalogPackageManagerActions.js
@@ -21,7 +21,17 @@
class CatalogPackageManagerActions {
constructor() {
- this.generateActions('downloadCatalogPackage', 'downloadCatalogPackageStatusUpdated', 'downloadCatalogPackageError', 'uploadCatalogPackage', 'uploadCatalogPackageStatusUpdated', 'uploadCatalogPackageError', 'removeCatalogPackage');
+ this.generateActions(
+ 'downloadCatalogPackage',
+ 'downloadCatalogPackageStatusUpdated',
+ 'downloadCatalogPackageError',
+ 'uploadCatalogPackage',
+ 'uploadCatalogPackageStatusUpdated',
+ 'uploadCatalogPackageError',
+ 'copyCatalogPackage',
+ 'updateStatus',
+ 'removeCatalogOperation'
+ );
}
}
diff --git a/skyquake/plugins/composer/src/src/components/CatalogPackageManager.js b/skyquake/plugins/composer/src/src/components/CatalogPackageManager.js
index c0b996c..0811093 100644
--- a/skyquake/plugins/composer/src/src/components/CatalogPackageManager.js
+++ b/skyquake/plugins/composer/src/src/components/CatalogPackageManager.js
@@ -88,7 +88,7 @@
var createItem = function (catalogPackage) {
const onClickRemove = function () {
- CatalogPackageManagerActions.removeCatalogPackage(catalogPackage);
+ CatalogPackageManagerActions.removeCatalogOperation(catalogPackage);
};
const classNames = ClassNames('item', {'-error': catalogPackage.error, '-success': catalogPackage.success});
return (
@@ -106,11 +106,11 @@
);
};
- const packages = this.state.packages || [];
+ const operations = this.state.operations || [];
return (
<div className="CatalogPackageManager">
<div className="items">
- {packages.map(createItem)}
+ {operations.map(createItem)}
</div>
</div>
);
diff --git a/skyquake/plugins/composer/src/src/components/CatalogPanelToolbar.js b/skyquake/plugins/composer/src/src/components/CatalogPanelToolbar.js
index 5e3e3c0..1501ecd 100644
--- a/skyquake/plugins/composer/src/src/components/CatalogPanelToolbar.js
+++ b/skyquake/plugins/composer/src/src/components/CatalogPanelToolbar.js
@@ -94,6 +94,7 @@
CatalogItemsActions.createCatalogItem(type);
},
onClickDuplicateCatalogItem() {
+ CatalogPanelTrayActions.open();
CatalogItemsActions.duplicateSelectedCatalogItem();
},
onClickExportCatalogItems() {
diff --git a/skyquake/plugins/composer/src/src/sources/CatalogPackageManagerSource.js b/skyquake/plugins/composer/src/src/sources/CatalogPackageManagerSource.js
index 48d697b..290d715 100644
--- a/skyquake/plugins/composer/src/src/sources/CatalogPackageManagerSource.js
+++ b/skyquake/plugins/composer/src/src/sources/CatalogPackageManagerSource.js
@@ -18,71 +18,98 @@
*/
'use strict';
-import $ from 'jquery'
import alt from '../alt'
-import utils from '../libraries/utils'
+import catalogUtils from '../libraries/utils'
import CatalogPackageManagerActions from '../actions/CatalogPackageManagerActions'
-let Utils = require('utils/utils.js');
-function getApiServerOrigin() {
- return utils.getSearchParams(window.location).upload_server + ':4567';
-}
+import Utils from 'utils/utils.js';
-function ajaxRequest(path, catalogPackage, resolve, reject, method = 'GET', input, urlOverride) {
- let options = {
- url: getApiServerOrigin() + path,
- type: method,
- beforeSend: Utils.addAuthorizationStub,
- dataType: 'json',
- success: function(data) {
- if (typeof data == 'string') {
- data = JSON.parse(data);
- }
- resolve({
- data: data,
- state: catalogPackage
- });
- },
- error: function(error) {
- if (typeof error == 'string') {
- error = JSON.parse(error);
- }
- reject({
- data: error,
- state: catalogPackage
- });
- }
+const getAuthorization = () => 'Basic ' + window.sessionStorage.getItem("auth");
+
+const getStateApiPath = (operation, id) =>
+ catalogUtils.getSearchParams(window.location).upload_server + ':4567/api/' + operation + '/' + id + '/state';
+
+const getComposerApiPath = (api) =>
+ window.location.origin + '/composer/api/' + api + '?api_server=' + catalogUtils.getSearchParams(window.location).api_server;
+
+const SUCCESS = {
+ pending: false,
+ success: true,
+ error: false,
+ message: "Completely successfully"
};
- if(input) {
- options.data = input;
+const FAILED = {
+ pending: false,
+ success: false,
+ error: true,
+ message: "Failed"
+ };
+const PENDING = {
+ pending: true,
+ success: false,
+ error: false,
+ message: "In progress"
+ };
+
+function ajaxFetch(url, operation, resolve, reject, method = 'GET', input, urlOverride) {
+ let credentials = 'same-origin';
+ let body = input ? JSON.stringify(input) : null;
+ let headers = new Headers();
+ headers.append('Authorization', getAuthorization());
+ headers.append('Accept', 'application/json');
+ if (input) {
+ headers.append('Content-Type', 'application/json');
}
- if (urlOverride) {
- options.url = window.location.origin + path;
+
+ fetch(url, {method, credentials, headers, body})
+ .then(checkStatusGetJson)
+ .then(handleSuccess)
+ .catch(handleError);
+
+ function checkStatusGetJson(response) {
+ if (response.status >= 200 && response.status < 300) {
+ return response.json();
+ } else {
+ var error = new Error(response.statusText)
+ error.status = response.status;
+ error.statusText = response.statusText;
+ throw error
+ }
}
- $.ajax(options).fail(function(xhr){
- //Authentication and the handling of fail states should be wrapped up into a connection class.
- Utils.checkAuthentication(xhr.status);
- });
+
+ function handleSuccess (data) {
+ if (typeof data == 'string') {
+ data = JSON.parse(data);
+ }
+ resolve({
+ state: operation,
+ data,
+ operation
+ });
+ }
+
+ function handleError (data) {
+ if (typeof data == 'string') {
+ data = JSON.parse(data);
+ }
+ reject({
+ state: operation,
+ data,
+ operation
+ });
+ }
}
-
-
const CatalogPackageManagerSource = {
- requestCatalogPackageDownload: function () {
+ requestCatalogPackageDownload: function () {
return {
remote: function (state, download, format, grammar, schema) {
return new Promise((resolve, reject) => {
- // the server does not add a status in the payload
- // so we add one so that the success handler will
- // be able to follow the flow of this download
- const setStatusBeforeResolve = (response = {}) => {
+ // we need an initial status for UI (server does not send)
+ const setStatusBeforeResolve = (response) => {
response.data.status = 'download-requested';
resolve(response);
};
- // RIFT-13485 requires to send type (nsd/vnfd) as a path element.
- // Backend no longer supports mixed multi-package download.
- // Probably does not even support multi-package download of same type.
- // Hence, pick the type from the first element.
const data = {
"package-type": download['catalogItems'][0]['uiState']['type'].toUpperCase(),
"package-id": download.ids,
@@ -90,13 +117,9 @@
"export-grammar": grammar && grammar.toUpperCase() || 'OSM',
"export-schema": schema && schema.toUpperCase() || "RIFT"
}
- const path = "/composer/api/package-export?api_server=" + utils.getSearchParams(window.location).api_server;
- ajaxRequest(path, download, setStatusBeforeResolve, reject, 'POST', data, true);
+ const path = getComposerApiPath('package-export');
+ ajaxFetch(path, download, setStatusBeforeResolve, reject, 'POST', data, true);
})
- //.then(function(data) {
- // let filename = data.data.output.filename;
- // window.open(getApiServerOrigin() + "/api/export/" + filename, "_blank")
- //});
},
success: CatalogPackageManagerActions.downloadCatalogPackageStatusUpdated,
error: CatalogPackageManagerActions.downloadCatalogPackageError
@@ -108,8 +131,8 @@
remote: function(state, download) {
const transactionId = download.transactionId;
return new Promise(function(resolve, reject) {
- const path = '/api/export/' + transactionId + '/state';
- ajaxRequest(path, download, resolve, reject);
+ const path = getStateApiPath('export', transactionId);
+ ajaxFetch(path, download, resolve, reject);
});
},
success: CatalogPackageManagerActions.downloadCatalogPackageStatusUpdated,
@@ -117,14 +140,69 @@
}
},
+ requestCatalogPackageCopy: function () {
+ return {
+ remote: function (state, operationInfo) {
+ return new Promise((resolve, reject) => {
+ // we need an initial status for UI (server does not send)
+ const successHandler = (response) => {
+ const status = response.data.output.status;
+ const state = status === "COMPLETED" ? SUCCESS : status === "FAILED" ? FAILED : PENDING;
+ state.progress = 25; // put something
+ let operation = Object.assign({}, operationInfo, state);
+ operation.transactionId = response.data.output['transaction-id'];
+ resolve(operation);
+ }
+ const failHandler = (response) => {
+ let operation = Object.assign({}, this, FAILED);
+ reject(operation);
+ };
+ const data = {
+ "package-type": operationInfo.packageType,
+ "package-id": operationInfo.id,
+ "package-name": operationInfo.name
+ }
+ const path = getComposerApiPath('package-copy');
+ ajaxFetch(path, operationInfo, successHandler, failHandler, 'POST', data, true);
+ })
+ },
+ success: CatalogPackageManagerActions.updateStatus,
+ error: CatalogPackageManagerActions.updateStatus
+ };
+ },
+
+ requestCatalogPackageCopyStatus: function() {
+ return {
+ remote: function(state, operation) {
+ return new Promise(function(resolve, reject) {
+ const successHandler = (response) => {
+ const status = response.data[0].status;
+ const state = status === "COMPLETED" ? SUCCESS : status === "FAILED" ? FAILED : PENDING;
+ state.progress = state.pending ? operation.progress + ((100 - operation.progress) / 2) : 100;
+ let newOp = Object.assign({}, operation, state);
+ resolve(newOp);
+ };
+ const failHandler = (response) => {
+ let operation = Object.assign({}, this, FAILED);
+ reject(operation);
+ };
+ const path = getComposerApiPath('package-manager/jobs/' + operation.transactionId);
+ ajaxFetch(path, operation, successHandler, failHandler);
+ });
+ },
+ success: CatalogPackageManagerActions.updateStatus,
+ error: CatalogPackageManagerActions.updateStatus
+ }
+ },
+
requestCatalogPackageUploadStatus: function () {
return {
remote: function (state, upload) {
const transactionId = upload.transactionId;
return new Promise(function (resolve, reject) {
const action = upload.riftAction === 'onboard' ? 'upload' : 'update';
- const path = '/api/' + action + '/' + transactionId + '/state';
- ajaxRequest(path, upload, resolve, reject);
+ const path = getStateApiPath(action, transactionId);
+ ajaxFetch(path, upload, resolve, reject);
});
},
success: CatalogPackageManagerActions.uploadCatalogPackageStatusUpdated,
diff --git a/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js b/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js
index 3e4ac7f..06d1342 100644
--- a/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js
+++ b/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js
@@ -460,27 +460,10 @@
}
duplicateSelectedCatalogItem() {
- const item = this.getFirstSelectedCatalogItem();
- if (item) {
- const newItem = _cloneDeep(item);
- newItem.name = newItem.name + ' Copy';
- newItem.id = guid();
- UID.assignUniqueId(newItem.uiState);
- const nsd = this.addNewItemToCatalog(newItem);
- this.selectCatalogItem(nsd);
- nsd.uiState.isNew = true;
- nsd.uiState.modified = true;
- nsd.uiState['instance-ref-count'] = 0;
- // note duplicated items get a new id, map the layout position
- // of the old id to the new id in order to preserve the layout
- if (nsd.uiState.containerPositionMap) {
- nsd.uiState.containerPositionMap[nsd.id] = nsd.uiState.containerPositionMap[item.id];
- delete nsd.uiState.containerPositionMap[item.id];
- }
- setTimeout(() => {
- this.selectCatalogItem(nsd);
- CatalogItemsActions.editCatalogItem.defer(nsd);
- }, 200);
+ // make request to backend to duplicate an item
+ const srcItem = this.getFirstSelectedCatalogItem();
+ if (srcItem) {
+ CatalogPackageManagerActions.copyCatalogPackage.defer(srcItem);
}
}
diff --git a/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js b/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js
index 5ffe83f..c964c67 100644
--- a/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js
+++ b/skyquake/plugins/composer/src/src/stores/CatalogPackageManagerStore.js
@@ -32,8 +32,21 @@
import imgDownload from '../../../node_modules/open-iconic/svg/cloud-download.svg'
import imgOnboard from '../../../node_modules/open-iconic/svg/cloud-upload.svg'
import imgUpdate from '../../../node_modules/open-iconic/svg/data-transfer-upload.svg'
+import imgCopy from '../../../node_modules/open-iconic/svg/layers.svg'
const defaults = {
+ operation: {
+ id: '',
+ name: '',
+ icon: '',
+ transactionId: '',
+ progress: 0,
+ message: 'Requested',
+ args: {},
+ pending: false,
+ success: false,
+ error: false,
+ },
downloadPackage: {
id: '',
name: '',
@@ -60,13 +73,13 @@
return utils.getSearchParams(window.location).upload_server + ':4567';
}
-function delayStatusCheck(statusCheckFunction, catalogPackage) {
- if (!catalogPackage.checkStatusTimeoutId) {
+function delayStatusCheck(statusCheckFunction, operation) {
+ if (!operation.checkStatusTimeoutId) {
const delayCallback = function () {
- delete catalogPackage.checkStatusTimeoutId;
- statusCheckFunction(catalogPackage).catch(exception);
+ delete operation.checkStatusTimeoutId;
+ statusCheckFunction(operation).catch(exception);
};
- catalogPackage.checkStatusTimeoutId = _delay(delayCallback, defaults.checkStatusDelayInSeconds * 1000);
+ operation.checkStatusTimeoutId = _delay(delayCallback, defaults.checkStatusDelayInSeconds * 1000);
}
}
@@ -74,52 +87,76 @@
constructor() {
- this.packages = [];
+ this.operations = [];
this.registerAsync(CatalogDataSource);
this.registerAsync(CatalogPackageManagerSource);
- this.bindAction(CatalogPackageManagerActions.REMOVE_CATALOG_PACKAGE, this.removeCatalogPackage);
+ this.bindAction(CatalogPackageManagerActions.REMOVE_CATALOG_OPERATION, this.removeCatalogOperation);
this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE, this.downloadCatalogPackage);
this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onDownloadCatalogPackageStatusUpdated);
this.bindAction(CatalogPackageManagerActions.DOWNLOAD_CATALOG_PACKAGE_ERROR, this.onDownloadCatalogPackageError);
this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE, this.uploadCatalogPackage);
this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_STATUS_UPDATED, this.onUploadCatalogPackageStatusUpdated);
this.bindAction(CatalogPackageManagerActions.UPLOAD_CATALOG_PACKAGE_ERROR, this.onUploadCatalogPackageError);
-
+ this.bindAction(CatalogPackageManagerActions.COPY_CATALOG_PACKAGE, this.copyCatalogPackage);
+ this.bindAction(CatalogPackageManagerActions.UPDATE_STATUS, this.updateOperationStatus);
}
- addPackage(catalogPackage) {
- const packages = [catalogPackage].concat(this.packages);
- this.setState({packages: packages});
+ addOperation(operation) {
+ const operations = [operation].concat(this.operations);
+ this.setState({operations});
}
- updatePackage(catalogPackage) {
- const packages = this.packages.map(d => {
- if (d.id === catalogPackage.id) {
- return Object.assign({}, d, catalogPackage);
+ updateOperation(operation) {
+ const operations = this.operations.map(d => {
+ if (d.id === operation.id) {
+ return Object.assign({}, d, operation);
}
return d;
});
- this.setState({packages: packages});
+ this.setState({operations});
}
- removeCatalogPackage(catalogPackage) {
- const packages = this.packages.filter(d => d.id !== catalogPackage.id);
- this.setState({packages: packages});
+ removeCatalogOperation(operation) {
+ const operations = this.operations.filter(d => d.id !== operation.id);
+ this.setState({operations});
+ }
+
+ copyCatalogPackage(sourcePackage) {
+ let operationInfo = Object.assign({}, defaults.operation);
+ operationInfo.name = "Duplication of " + sourcePackage.name;
+ operationInfo.id = guid();
+ operationInfo.icon = imgCopy;
+ operationInfo.type = 'copy';
+ operationInfo.message = 'Requesting package duplication.';
+ operationInfo.args.packageType = sourcePackage['uiState']['type'].toUpperCase();
+ operationInfo.args.id = sourcePackage.id;
+ operationInfo.args.name = sourcePackage.name + ' copy';
+
+ this.addOperation(operationInfo);
+ this.getInstance().requestCatalogPackageCopy(operationInfo, sourcePackage);
+ }
+
+ updateOperationStatus(operation) {
+ console.debug('package manager operation status update', operation);
+ this.updateOperation(operation);
+ if (operation.pending) {
+ delayStatusCheck(this.getInstance().requestCatalogPackageCopyStatus, operation);
+ }
}
uploadCatalogPackage(file) {
file.id = file.id || guid();
- const catalogPackage = _pick(file, packagePropertyNames);
- catalogPackage.icon = file.riftAction === 'onboard' ? imgOnboard : imgUpdate;
- catalogPackage.type = 'upload';
- this.addPackage(catalogPackage);
+ const operation = _pick(file, packagePropertyNames);
+ operation.icon = file.riftAction === 'onboard' ? imgOnboard : imgUpdate;
+ operation.type = 'upload';
+ this.addOperation(operation);
// note DropZone.js handles the async upload so we don't have to invoke any async action creators
}
onUploadCatalogPackageStatusUpdated(response) {
const upload = updateStatusInfo(response);
- this.updatePackage(upload);
+ this.updateOperation(upload);
console.log('updating package upload')
// if pending with no transaction id - do nothing
// bc DropZone.js will notify upload progress
@@ -134,8 +171,8 @@
onUploadCatalogPackageError(response) {
console.warn('onUploadCatalogPackageError', response);
- const catalogPackage = updateStatusInfo(response);
- this.updatePackage(catalogPackage);
+ const operation = updateStatusInfo(response);
+ this.updateOperation(operation);
}
downloadCatalogPackage(data) {
@@ -144,22 +181,22 @@
let grammar = data['selectedGrammar'] || 'osm';
let format = "YAML";
if (catalogItems.length) {
- const catalogPackage = Object.assign({}, defaults.downloadPackage, {id: guid()});
- catalogPackage.name = catalogItems[0].name;
- catalogPackage.type = 'download';
+ const operation = Object.assign({}, defaults.downloadPackage, {id: guid()});
+ operation.name = catalogItems[0].name;
+ operation.type = 'download';
if (catalogItems.length > 1) {
- catalogPackage.name += ' (' + catalogItems.length + ' items)';
+ operation.name += ' (' + catalogItems.length + ' items)';
}
- catalogPackage.ids = catalogItems.map(d => d.id).sort().toString();
- catalogPackage.catalogItems = catalogItems;
- this.addPackage(catalogPackage);
- this.getInstance().requestCatalogPackageDownload(catalogPackage, format, grammar, schema).catch(exception);
+ operation.ids = catalogItems.map(d => d.id).sort().toString();
+ operation.catalogItems = catalogItems;
+ this.addOperation(operation);
+ this.getInstance().requestCatalogPackageDownload(operation, format, grammar, schema).catch(exception);
}
}
onDownloadCatalogPackageStatusUpdated(response) {
const download = updateStatusInfo(response);
- this.updatePackage(download);
+ this.updateOperation(download);
if (download.pending) {
delayStatusCheck(this.getInstance().requestCatalogPackageDownloadStatus, download);
}
@@ -167,8 +204,8 @@
onDownloadCatalogPackageError(response) {
console.warn('onDownloadCatalogPackageError', response);
- const catalogPackage = updateStatusInfo(response);
- this.updatePackage(catalogPackage);
+ const operation = updateStatusInfo(response);
+ this.updateOperation(operation);
}
}
@@ -188,26 +225,26 @@
}
function updateStatusInfo(response) {
- // returns the catalogPackage object with the status fields updated based on the server response
+ // returns the operation object with the status fields updated based on the server response
const statusInfo = {
pending: false,
success: false,
error: false
};
const responseData = (response.data.output) ? response.data.output : response.data;
- const catalogPackage = response.state;
+ const operation = response.state;
if ( typeof response.data.status !== "number" ) {
switch(response.data.status) {
case 'upload-progress':
statusInfo.pending = true;
statusInfo.progress = parseFloat(responseData.progress) || 0;
- statusInfo.message = calculateUploadProgressMessage(catalogPackage.size, responseData.progress, responseData.bytesSent);
+ statusInfo.message = calculateUploadProgressMessage(operation.size, responseData.progress, responseData.bytesSent);
break;
case 'upload-success':
statusInfo.pending = true;
statusInfo.progress = 100;
statusInfo.message = 'Upload completed.';
- statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || catalogPackage.transactionId;
+ statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || operation.transactionId;
break;
case 'upload-error':
statusInfo.error = true;
@@ -216,7 +253,7 @@
case 'download-requested':
statusInfo.pending = true;
statusInfo.progress = 25;
- statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || catalogPackage.transactionId;
+ statusInfo.transactionId = responseData['transaction_id'] || responseData['transaction-id'] || operation.transactionId;
break;
case 'pending':
statusInfo.pending = true;
@@ -227,12 +264,12 @@
statusInfo.success = true;
statusInfo.progress = 100;
statusInfo.message = responseData.events[responseData.events.length - 1].text;
- if (catalogPackage.type === 'download') {
+ if (operation.type === 'download') {
statusInfo.urlValidUntil = moment().add(defaults.downloadUrlTimeToLiveInMinutes, 'minutes').toISOString();
if (responseData.filename) {
statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + responseData.filename;
} else {
- statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + catalogPackage.transactionId + '.tar.gz';
+ statusInfo.url = getCatalogPackageManagerServerOrigin() + '/api/export/' + operation.transactionId + '.tar.gz';
}
}
break;
@@ -248,7 +285,7 @@
statusInfo.error = true;
statusInfo.message = responseData.statusText || 'Error';
}
- return Object.assign({}, catalogPackage, statusInfo);
+ return Object.assign({}, operation, statusInfo);
}
export default alt.createStore(CatalogPackageManagerStore, 'CatalogPackageManagerStore');