Merge "RIFT-15943 - Debug tab presented no content and download button broken - the...
[osm/UI.git] / skyquake / plugins / debug / src / crash.jsx
1
2 /*
3  * 
4  *   Copyright 2016 RIFT.IO Inc
5  *
6  *   Licensed under the Apache License, Version 2.0 (the "License");
7  *   you may not use this file except in compliance with the License.
8  *   You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *   Unless required by applicable law or agreed to in writing, software
13  *   distributed under the License is distributed on an "AS IS" BASIS,
14  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *   See the License for the specific language governing permissions and
16  *   limitations under the License.
17  *
18  */
19 import React from 'react';
20 import './crash.scss';
21 import TreeView from 'react-treeview';
22 import '../node_modules/react-treeview/react-treeview.css';
23 import AppHeader from 'widgets/header/header.jsx';
24 import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
25 var crashActions = require('./crashActions.js');
26 var crashStore = require('./crashStore.js');
27 // var MissionControlStore = require('../missioncontrol/missionControlStore.js');
28 function openDashboard() {
29     window.location.hash = "#/";
30 }
31 class CrashDetails extends React.Component {
32     constructor(props) {
33         super(props)
34         this.state = crashStore.getState();
35         crashStore.listen(this.storeListener);
36     }
37     storeListener = (data) => {
38         this.setState({
39             isLoading: data.isLoading,
40             list: data.crashList,
41             noDebug: !this.hasDebugData(data.crashList)
42         });
43     }
44     componentWillUnmount() {
45         crashStore.unlisten(this.storeListener);
46     }
47     componentWillMount() {
48         crashStore.get();
49     }
50     hasDebugData(list) {
51         console.log(list);
52         if (list && list.length > 0) {
53             for (let i = 0; i < list.length; i++) {
54                 var trace = list[i].backtrace;
55                 for (let j = 0; j < trace.length; j++) {
56                     console.log(trace[j])
57                     if (trace[j].detail) {
58                         return true;
59                     }
60                 }
61             }
62         }
63         return false;
64     }
65     downloadFile(fileName) {
66         // wait till download selected to create text blob
67         let urlData = JSON.stringify(this.state.list, null, 2);
68         let replacedNewLines = urlData.replace(/\\n/g, '\n');
69         let replacedTabs = replacedNewLines.replace(/\\t/g, '\t');
70         let replacedQuotes = replacedTabs.replace(/\\"/g, '"');
71         let textFileBlob = new Blob([replacedQuotes], { type: 'text/plain;charset=UTF-8' });
72         let aLink = document.createElement('a');
73         aLink.download = fileName;
74         aLink.href = window.URL.createObjectURL(textFileBlob);
75         aLink.click(); // suprise, this works without being on the page
76         // it seems to cause no problems cleaning up the blob right away
77         window.URL.revokeObjectURL(aLink.href);
78         // assuming aLink just goes away when this function ends
79     }
80     render() {
81         let html;
82         if (this.state != null) {
83             if (!this.state.noDebug) {
84                 let tree = this.state.list && this.state.list.map((node, i) => {
85                     const vm = node.name;
86                     const vm_label = <span>{vm}</span>;
87                     const backtrace = node.backtrace;
88                     return (
89                         <TreeView key={vm + '|' + i} nodeLabel={vm_label} defaultCollapsed={false}>
90                             {backtrace.map(details => {
91                                 // do some trickery to normalize details 'new line' char(s)
92                                 let textareaElement = document.createElement("textarea")
93                                 textareaElement.innerHTML = details.detail;
94                                 let detailsFormatted = textareaElement.value;
95                                 let arr = detailsFormatted.split(/\n/);
96                                 let text = [];
97                                 for (let i = 0; i < arr.length; i++) {
98                                     text.push(arr[i]);
99                                     text.push(<br key={'line-' + i} />); // react likes keys on array children
100                                 }
101
102                                 return (
103                                     <TreeView nodeLabel={<span>{details.id}</span>} key={vm + '||' + details.id} defaultCollapsed={false}>
104                                         <p>{text}</p>
105                                     </TreeView>
106                                 );
107                             })}
108                         </TreeView>
109                     );
110                 });
111                 let doDownload = this.downloadFile.bind(this, 'crash.txt');
112                 html = (
113                     <div className="crash-details-wrapper">
114                         <div className="form-actions">
115                             <button role="button" className="dark" onClick={this.state.noDebug ? false : doDownload}>Download Crash Details</button>
116                         </div>
117                         <div className="crash-container">
118                             <h2> Debug Information </h2>
119                             <div className="tree">{tree}</div>
120                         </div>
121                     </div>
122                 );
123             } else {
124                 let text = this.state.isLoading ? "Loading Debug Information" : "No Debug Information Available"
125                 html = (
126                     <div className="crash-details-wrapper">
127                         <div className="crash-container">
128                             <div style={{ 'marginLeft': 'auto', 'marginRight': 'auto', 'width': '230px', 'padding': '90px' }}>{text}</div>
129                         </div>
130                     </div>
131                 );
132             }
133         } else {
134             html = <div className="crash-container"></div>
135         };
136         let refPage = window.sessionStorage.getItem('refPage');
137         refPage = JSON.parse(refPage);
138         return (
139             <div className="crash-app">
140                 {html}
141                 <ScreenLoader show={this.state.isLoading}/> 
142             </div>
143         );
144     }
145 }
146 export default CrashDetails;