Merge "RIFT-15944 - editing an ‘id’ field is troublesome as focus is lost on each char typed - fixed use of id as html element id - bit of code clean up - made use of knowing when a ‘key’ on a component needs to be unique (only amongst siblings)"
diff --git a/skyquake/framework/widgets/header/header.jsx b/skyquake/framework/widgets/header/header.jsx
index 21f4c10..3cfaaf6 100644
--- a/skyquake/framework/widgets/header/header.jsx
+++ b/skyquake/framework/widgets/header/header.jsx
@@ -1,5 +1,5 @@
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,6 +55,7 @@
type={"error"}
hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
onDismiss={HeaderStore.validateReset}
+ timeout= {5000}
/>
// html = (
diff --git a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
index d53d85f..d83c836 100644
--- a/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
+++ b/skyquake/framework/widgets/skyquake_container/skyquakeContainer.jsx
@@ -106,6 +106,7 @@
type={notificationType}
hidden={!(displayNotification && notificationMessage)}
onDismiss={SkyquakeContainerActions.hideNotification}
+ timeout= {5000}
/>
<ScreenLoader show={displayScreenLoader}/>
<SkyquakeNav nav={this.state.nav}
diff --git a/skyquake/plugins/accounts/src/account/account.jsx b/skyquake/plugins/accounts/src/account/account.jsx
index e588e74..b7dbf35 100644
--- a/skyquake/plugins/accounts/src/account/account.jsx
+++ b/skyquake/plugins/accounts/src/account/account.jsx
@@ -169,7 +169,7 @@
}
evaluateSubmit = (e) => {
if (e.keyCode == 13) {
- if (this.props.edit) {
+ if (this.props.params.name != 'create') {
this.update(e);
} else {
this.create(e);
diff --git a/skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx b/skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx
index d1e5c93..ed9ea93 100644
--- a/skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx
+++ b/skyquake/plugins/composer/src/src/components/filemanager/FileManager.jsx
@@ -124,7 +124,7 @@
function buildList(self, data) {
let toReturn = [];
data.id.map(function(k,i) {
- toReturn.push (contentFolder(self, data.data[k], k, i, self.props.filesState, self.updateFileLocationInput, self.sendDownloadFileRequst, self.deleteFile));
+ toReturn.push (contentFolder(self, data.data[k], k, k+i, self.props.filesState, self.updateFileLocationInput, self.sendDownloadFileRequst, self.deleteFile));
});
return toReturn.reverse();
}
@@ -132,7 +132,8 @@
function contentFolder(context, folder, path, key, inputState, updateFn, sendDownloadFileRequst, deleteFn) {
let type = context.props.type;
let id = context.props.item.id;
- const onboardDropZone = createDropZone.bind(this, FileManagerUploadDropZone.ACTIONS.onboard, '.ComposerAppAddFile.' + path.replace(/\//g, '-'), type, id, path);
+ let classId = `DZ-${path.replace(/\/|\s+/g, '-')}`;
+ const onboardDropZone = createDropZone.bind(this, FileManagerUploadDropZone.ACTIONS.onboard, '.ComposerAppAddFile.' + classId, type, id, path);
return (
<Panel title={path} key={key} itemClassName="nested" no-corners>
<div className="folder">
@@ -170,11 +171,12 @@
}
render() {
let {type, id, path, key, ...props} = this.props;
+ let classId = `DZ-${path.replace(/\/|\s+/g, '-')}`;
return (
<div className="inputSection">
<label className="sqTextInput" style={{flexDirection: 'row', alignItems:'center'}}>
<span>Upload File</span>
- <Button className={'ComposerAppAddFile ' + path.replace(/\//g, '-')} label="BROWSE"/>
+ <Button className={'ComposerAppAddFile ' + classId} label="BROWSE"/>
</label>
</div>
)
diff --git a/skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js b/skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js
index 4b36277..1f0fd80 100644
--- a/skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js
+++ b/skyquake/plugins/composer/src/src/components/filemanager/FileManagerSource.js
@@ -100,16 +100,26 @@
beforeSend: Utils.addAuthorizationStub,
url: 'api/file-manager?api_server=' + utils.getSearchParams(window.location).api_server +'&package_type=' + type + '&package_id=' + id + '&package_path=' + path ,
success: function(data) {
- resolve({
- data: data,
- path: path
- });
+ if (data.output.status == 'True') {
+ resolve({
+ data: data,
+ path: path
+ });
+ } else {
+ reject({
+ data: data,
+ path: path
+ })
+ }
},
error: function(error) {
if (typeof error == 'string') {
error = JSON.parse(error);
}
- reject(error);
+ reject({
+ path: path,
+ data: error
+ });
}
}).fail(function(xhr){
//Authentication and the handling of fail states should be wrapped up into a connection class.
diff --git a/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js b/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js
index 9345bc3..3e4ac7f 100644
--- a/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js
+++ b/skyquake/plugins/composer/src/src/stores/CatalogDataStore.js
@@ -1,6 +1,6 @@
/*
- *
+ *
* Copyright 2016 RIFT.IO Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -591,6 +591,13 @@
this.resetSelectionState();
}
}
+ saveCatalogItemError(data){
+ let error = JSON.parse(data.error.responseText);
+ const errorMsg = error && error.body && error.body['rpc-reply'] && JSON.stringify(error.body['rpc-reply']['rpc-error'], null, ' ')
+ ComposerAppActions.showError.defer({
+ errorMessage: 'Unable to save the descriptor.\n' + errorMsg
+ });
+ }
}
export default alt.createStore(CatalogDataStore, 'CatalogDataStore');
diff --git a/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js b/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
index c07fffd..c677a44 100644
--- a/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
+++ b/skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
@@ -172,6 +172,7 @@
addFileSuccess: FileManagerActions.addFileSuccess,
deletePackageFile: FileManagerActions.deletePackageFile,
deleteFileSuccess: FileManagerActions.deleteFileSuccess,
+ deleteFileError: FileManagerActions.deleteFileError,
closeFileManagerSockets: FileManagerActions.closeFileManagerSockets,
openFileManagerSockets: FileManagerActions.openFileManagerSockets,
openDownloadMonitoringSocketSuccess: FileManagerActions.openDownloadMonitoringSocketSuccess,
@@ -681,6 +682,15 @@
files: files
})
}
+ deleteFileError = (error) => {
+ const filepath = error.path;
+ const message = error.data && error.data.output ? ' (' + error.data.output['error-trace'] + ')' : ' (server error)';
+ console.log('Unable to delete', filepath, 'Error:', message);
+ ComposerAppActions.showError.defer({
+ errorMessage: 'Unable to delete ' + filepath + message + '. '
+ });
+ }
+
newPathNameUpdated = (event) => {
const value = event.target.value;
this.setState({
diff --git a/skyquake/plugins/debug/src/crash.jsx b/skyquake/plugins/debug/src/crash.jsx
index 6c659c3..bed1dbf 100644
--- a/skyquake/plugins/debug/src/crash.jsx
+++ b/skyquake/plugins/debug/src/crash.jsx
@@ -21,114 +21,126 @@
import TreeView from 'react-treeview';
import '../node_modules/react-treeview/react-treeview.css';
import AppHeader from 'widgets/header/header.jsx';
+import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
var crashActions = require('./crashActions.js');
var crashStore = require('./crashStore.js');
// var MissionControlStore = require('../missioncontrol/missionControlStore.js');
function openDashboard() {
- window.location.hash = "#/";
+ window.location.hash = "#/";
}
class CrashDetails extends React.Component {
- constructor(props) {
- super(props)
- var self = this;
- this.state = crashStore.getState();
- crashStore.listen(this.storeListener);
- }
- storeListener = (data) => {
- this.setState({
- list:data.crashList,
- noDebug:!this.hasDebugData(data.crashList)
- });
- }
- componentWillUnmount(){
- crashStore.unlisten(this.storeListener);
- }
- componentWillMount() {
- crashStore.get();
- }
- hasDebugData(list) {
- console.log(list);
- if (list && list.length > 0) {
- for (let i = 0; i < list.length; i++) {
- var trace = list[i].backtrace;
- for (let j = 0; j < trace.length; j++) {
- console.log(trace[j])
- if (trace[j].detail) {
- return true;
- }
- }
- }
+ constructor(props) {
+ super(props)
+ this.state = crashStore.getState();
+ crashStore.listen(this.storeListener);
}
- return false;
- }
- downloadFile(fileName, urlData) {
- var replacedNewLines = urlData.replace(/\\n/g, '\n');
- var replacedTabs = replacedNewLines.replace(/\\t/g, '\t');
- var replacedQuotes= replacedTabs.replace(/\\"/g, '"');
- var textFileBlob = new Blob([replacedQuotes], {type: 'text/plain;charset=UTF-8'});
- var aLink = document.createElement('a');
- var evt = document.createEvent("HTMLEvents");
- evt.initEvent("click");
- aLink.download = fileName;
- aLink.href = window.URL.createObjectURL(textFileBlob);
- aLink.dispatchEvent(evt);
- }
- render() {
- let html;
- var list = null;
- if (this.state != null) {
- var tree = <div style={{'marginLeft':'auto', 'marginRight':'auto', 'width':'230px', 'padding':'90px'}}> No Debug Information Available </div>;
- if (!this.state.noDebug)
- {
- var tree = this.state.list && this.state.list.map((node, i) => {
- var vm = node.name;
- var vm_label = <span>{vm}</span>;
- var backtrace = node.backtrace;
- return (
- <TreeView key={vm + '|' + i} nodeLabel={vm_label} defaultCollapsed={false}>
- {backtrace.map(details => {
+ storeListener = (data) => {
+ this.setState({
+ isLoading: data.isLoading,
+ list: data.crashList,
+ noDebug: !this.hasDebugData(data.crashList)
+ });
+ }
+ componentWillUnmount() {
+ crashStore.unlisten(this.storeListener);
+ }
+ componentWillMount() {
+ crashStore.get();
+ }
+ hasDebugData(list) {
+ console.log(list);
+ if (list && list.length > 0) {
+ for (let i = 0; i < list.length; i++) {
+ var trace = list[i].backtrace;
+ for (let j = 0; j < trace.length; j++) {
+ console.log(trace[j])
+ if (trace[j].detail) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+ downloadFile(fileName) {
+ // wait till download selected to create text blob
+ let urlData = JSON.stringify(this.state.list, null, 2);
+ let replacedNewLines = urlData.replace(/\\n/g, '\n');
+ let replacedTabs = replacedNewLines.replace(/\\t/g, '\t');
+ let replacedQuotes = replacedTabs.replace(/\\"/g, '"');
+ let textFileBlob = new Blob([replacedQuotes], { type: 'text/plain;charset=UTF-8' });
+ let aLink = document.createElement('a');
+ aLink.download = fileName;
+ aLink.href = window.URL.createObjectURL(textFileBlob);
+ aLink.click(); // suprise, this works without being on the page
+ // it seems to cause no problems cleaning up the blob right away
+ window.URL.revokeObjectURL(aLink.href);
+ // assuming aLink just goes away when this function ends
+ }
+ render() {
+ let html;
+ if (this.state != null) {
+ if (!this.state.noDebug) {
+ let tree = this.state.list && this.state.list.map((node, i) => {
+ const vm = node.name;
+ const vm_label = <span>{vm}</span>;
+ const backtrace = node.backtrace;
+ return (
+ <TreeView key={vm + '|' + i} nodeLabel={vm_label} defaultCollapsed={false}>
+ {backtrace.map(details => {
+ // do some trickery to normalize details 'new line' char(s)
+ let textareaElement = document.createElement("textarea")
+ textareaElement.innerHTML = details.detail;
+ let detailsFormatted = textareaElement.value;
+ let arr = detailsFormatted.split(/\n/);
+ let text = [];
+ for (let i = 0; i < arr.length; i++) {
+ text.push(arr[i]);
+ text.push(<br key={'line-' + i} />); // react likes keys on array children
+ }
- //Needed to decode HTML
- var text_temp = document.createElement("textarea")
- text_temp.innerHTML = details.detail;
- var text_temp = text_temp.value;
- var arr = text_temp.split(/\n/);
- var text = [];
- for (let i = 0; i < arr.length; i++) {
- text.push(arr[i]);
- text.push(<br/>)
- }
-
- return (
- <TreeView nodeLabel={<span>{details.id}</span>} key={vm + '||' + details.id} defaultCollapsed={false}>
- <p>{text}</p>
- </TreeView>
- );
- })}
- </TreeView>
- );
- })}
- html = (
- <div className="crash-details-wrapper">
- <div className="form-actions">
- <button role="button" className="dark" onClick={this.state.noDebug ? false : this.downloadFile.bind(this, 'crash.txt', 'data:text;charset=UTF-8,' + decodeURIComponent(JSON.stringify(this.state.list, null, 2)))}> Download Crash Details</button>
- </div>
- <div className="crash-container">
- <h2> Debug Information </h2>
- <div className="tree">{tree}</div>
- </div>
- </div>
- );
- } else {
- html = <div className="crash-container"></div>
- };
- let refPage = window.sessionStorage.getItem('refPage');
- refPage = JSON.parse(refPage);
- return (
+ return (
+ <TreeView nodeLabel={<span>{details.id}</span>} key={vm + '||' + details.id} defaultCollapsed={false}>
+ <p>{text}</p>
+ </TreeView>
+ );
+ })}
+ </TreeView>
+ );
+ });
+ let doDownload = this.downloadFile.bind(this, 'crash.txt');
+ html = (
+ <div className="crash-details-wrapper">
+ <div className="form-actions">
+ <button role="button" className="dark" onClick={this.state.noDebug ? false : doDownload}>Download Crash Details</button>
+ </div>
+ <div className="crash-container">
+ <h2> Debug Information </h2>
+ <div className="tree">{tree}</div>
+ </div>
+ </div>
+ );
+ } else {
+ let text = this.state.isLoading ? "Loading Debug Information" : "No Debug Information Available"
+ html = (
+ <div className="crash-details-wrapper">
+ <div className="crash-container">
+ <div style={{ 'marginLeft': 'auto', 'marginRight': 'auto', 'width': '230px', 'padding': '90px' }}>{text}</div>
+ </div>
+ </div>
+ );
+ }
+ } else {
+ html = <div className="crash-container"></div>
+ };
+ let refPage = window.sessionStorage.getItem('refPage');
+ refPage = JSON.parse(refPage);
+ return (
<div className="crash-app">
- {html}
+ {html}
+ <ScreenLoader show={this.state.isLoading}/>
</div>
- );
- }
+ );
+ }
}
export default CrashDetails;
diff --git a/skyquake/plugins/debug/src/crashStore.js b/skyquake/plugins/debug/src/crashStore.js
index 51857d5..0b04445 100644
--- a/skyquake/plugins/debug/src/crashStore.js
+++ b/skyquake/plugins/debug/src/crashStore.js
@@ -20,18 +20,29 @@
function crashStore () {
this.exportAsync(require('./crashSource.js'));
this.bindActions(require('./crashActions.js'));
+ this.isLoading = false;
+ this.crashList = null;
}
crashStore.prototype.getCrashDetailsSuccess = function(list) {
this.setState({
- crashList:list
+ isLoading: false,
+ crashList: list
})
- console.log('success', list)
+ console.log('Crash details load success', list)
};
crashStore.prototype.getCrashDetailsLoading = function(info) {
+ this.setState({
+ isLoading: true,
+ crashList: null,
+ })
console.log('Loading crash details...', info)
};
crashStore.prototype.getCrashDetailsFailure = function(info) {
+ this.setState({
+ isLoading: false,
+ error: info
+ })
console.log('Failed to retrieve crash/debug details', info)
};
diff --git a/skyquake/plugins/launchpad/api/launchpad.js b/skyquake/plugins/launchpad/api/launchpad.js
index 6fb0170..d74bb3e 100644
--- a/skyquake/plugins/launchpad/api/launchpad.js
+++ b/skyquake/plugins/launchpad/api/launchpad.js
@@ -790,8 +790,14 @@
try {
if (nsr["monitoring-param"]) {
nsr["monitoring-param"].map(function(m) {
- var vnfr = vnfrs[m["vnfr-id"]] || {};
+ // var vnfr = vnfrs[m["vnfr-id"]] || {};
+ // m["vnfr-name"] = vnfr['name'] ? vnfr['name'] : (vnfr['short-name'] ? vnfr['short-name'] : 'VNFR');
+ var groupTag = m['group-tag'];
+ var vnfrId = m['vnfr-mon-param-ref'] && m['vnfr-mon-param-ref'][0] && m['vnfr-mon-param-ref'][0]['vnfr-id-ref'];
+ var vnfr = vnfrs[vnfrId] || {};
m["vnfr-name"] = vnfr['name'] ? vnfr['name'] : (vnfr['short-name'] ? vnfr['short-name'] : 'VNFR');
+ m['group-tag'] = (groupTag ? (groupTag + ' - ') : '') + m['vnfr-name'] + (vnfrId ? ' (' + vnfrId.substring(1,8) + '...)' : '');
+
});
}
} catch (e) {