From: Bob Gallagher Date: Tue, 28 Mar 2017 21:41:31 +0000 (-0400) Subject: RIFT-15943 - Debug tab presented no content and download button broken X-Git-Tag: v2.0.0~19^2 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=5f6d87f16bf80c3042a4bd5474bb0654b757d973;p=osm%2FUI.git RIFT-15943 - Debug tab presented no content and download button broken - the link eventDispatch was not working — converted to link.click() - cleaned up download — removed prefix (no longer needed) — postponed building download content till button pressed - added isLoading support — retrieving crash log from server can take some time — display loading text on page when loading — use loading component - bit of code clean up — formatting — var -> let/const — comments Change-Id: I9aa154c115c5a8b73879347ecc6587595d4bd0fd Signed-off-by: Bob Gallagher --- diff --git a/skyquake/plugins/debug/src/crash.jsx b/skyquake/plugins/debug/src/crash.jsx index 6c659c3d7..bed1dbf63 100644 --- a/skyquake/plugins/debug/src/crash.jsx +++ b/skyquake/plugins/debug/src/crash.jsx @@ -21,114 +21,126 @@ import './crash.scss'; 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); + } + 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; } - 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 =
No Debug Information Available
; - if (!this.state.noDebug) - { - var tree = this.state.list && this.state.list.map((node, i) => { - var vm = node.name; - var vm_label = {vm}; - var backtrace = node.backtrace; - return ( - - {backtrace.map(details => { - - //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(
) - } + 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 = {vm}; + const backtrace = node.backtrace; + return ( + + {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(
); // react likes keys on array children + } - return ( - {details.id}} key={vm + '||' + details.id} defaultCollapsed={false}> -

{text}

-
- ); - })} -
- ); - })} - html = ( -
-
- -
-
-

Debug Information

-
{tree}
-
-
- ); - } else { - html =
- }; - let refPage = window.sessionStorage.getItem('refPage'); - refPage = JSON.parse(refPage); - return ( + return ( + {details.id}} key={vm + '||' + details.id} defaultCollapsed={false}> +

{text}

+
+ ); + })} +
+ ); + }); + let doDownload = this.downloadFile.bind(this, 'crash.txt'); + html = ( +
+
+ +
+
+

Debug Information

+
{tree}
+
+
+ ); + } else { + let text = this.state.isLoading ? "Loading Debug Information" : "No Debug Information Available" + html = ( +
+
+
{text}
+
+
+ ); + } + } else { + html =
+ }; + let refPage = window.sessionStorage.getItem('refPage'); + refPage = JSON.parse(refPage); + return (
- {html} + {html} +
- ); - } + ); + } } export default CrashDetails; diff --git a/skyquake/plugins/debug/src/crashStore.js b/skyquake/plugins/debug/src/crashStore.js index 51857d51f..0b04445be 100644 --- a/skyquake/plugins/debug/src/crashStore.js +++ b/skyquake/plugins/debug/src/crashStore.js @@ -20,18 +20,29 @@ import Alt from 'widgets/skyquake_container/skyquakeAltInstance'; 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) };