RIFT-14432: Composer choice/case fix
[osm/UI.git] / skyquake / plugins / logging / src / loggingGeneral.jsx
1  /*
2  * 
3  *   Copyright 2016 RIFT.IO Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  *
17  */
18 import React from 'react';
19 import _ from 'lodash';
20 import './logging.scss';
21
22 import Button from 'widgets/button/rw.button.js';
23 import DashboardCard from 'widgets/dashboard_card/dashboard_card.jsx';
24 import ScreenLoader from 'widgets/screen-loader/screenLoader.jsx';
25
26 import LoggingActions from './loggingActions.js';
27 import LoggingStore from './loggingStore.js';
28 import LoggingNav from './loggingNav.jsx';
29
30 import CategorySeverityGrid from './categorySeverityGrid.jsx';
31 import DenyEventsEditGroup from './denyEventsEditGroup.jsx';
32 import {DropList, RadioButtonGroup, CardSection } from './loggingWidgets.jsx';
33
34 import Crouton from 'react-crouton';
35 import 'style/common.scss';
36
37
38 class DefaultCategorySeverityPanel extends React.Component {
39
40   render() {
41     const {defaultSeverities, severities, ...props } = this.props;
42     return (
43       <DashboardCard className="defaultCategorySeverityPanel"
44         showHeader={true}
45         title="Default Category Severity">
46         <CategorySeverityGrid defaultSeverities={defaultSeverities}
47           severityOptions={severities}/>
48       </DashboardCard>
49     );
50   }
51 }
52
53
54 class LoggingEventsPanel extends React.Component {
55
56   handleChangeAllowDuplicateEvents(e) {
57     console.log("LoggingEventsPanel.handleChangeAllowDuplicateEvents:");
58     console.log("- e.currentTarget.value=", e.currentTarget.value);
59     console.log("- e.currentTarget=", e.currentTarget);
60
61     // NOTE, we may want to generalize our string to boolean convert/compare
62     let allowFlag = (e.currentTarget.value == 'true');
63     LoggingStore.updateAllowDuplicateEvents(allowFlag);
64   }
65
66   handleAddDenyEvent(e) {
67     LoggingStore.addDenyEvent(null);
68   }
69
70   render() {
71     const {allowDuplicateEvents, eventIDs, radioItems, ...props} = this.props;
72     let self = this;
73     let selectedIndex = allowDuplicateEvents ? 0 :1;
74
75     return (
76       <DashboardCard className="loggingEventsPanel"
77         showHeader={true}
78         title="Events">
79         <CardSection title={"Duplicates"}>
80           <div className="radioButtonGroupHeader">Allow duplicate events</div>
81           <RadioButtonGroup className="radioButtonGroup"
82             items={radioItems}
83             selectedItem={radioItems[selectedIndex]}
84             onChange={this.handleChangeAllowDuplicateEvents}
85             radioGroupName="allowDuplicateEvents"
86           />
87         </CardSection>
88         <CardSection title={"Deny"}>
89           <DenyEventsEditGroup className="denyEventsEditGroup"
90             eventIDs={eventIDs}
91             warnInvalidEventID={true}
92           />
93           <div className="plusButton" onClick={this.handleAddDenyEvent}>
94             <span className="oi" data-glyph="plus"
95                   title="Add event id" aria-hidden="true"></span>
96           </div>
97         </CardSection>
98       </DashboardCard>
99     );
100   }
101 }
102 LoggingEventsPanel.defaultProps = {
103   radioItems: [
104     { label: "Allow", value: true },
105     { label: "Deny", value: false }
106   ],
107   allowDuplicateEvents: false,
108   eventIDs: []
109 }
110
111 /**
112  *
113  */
114 class LoggingGeneralDetailsPanel extends React.Component {
115   constructor(props) {
116     super(props);
117     this.state = {
118       syslogViewerURL: props.syslogViewerURL
119     };
120   }
121
122   componentWillReceiveProps(props) {
123     this.setState({
124       syslogViewerURL: props.syslogViewerURL
125     });
126   }
127
128   handleUpdateDetailTextField(fieldName) {
129     let self = this;
130     return function(e) {
131       let state = {};
132       state[fieldName] = e.target.value;
133       self.setState(state);
134     }
135   }
136
137   handleUpdateSyslogViewerURL(e) {
138     LoggingStore.updateSyslogViewerURL(e.target.value);
139   }
140   handleOpenSysLogViewerURL(e) {
141     e.preventDefault();
142     window.open(this.state.syslogViewerURL);
143   }
144
145   render() {
146     return (
147       <DashboardCard className="loggingGeneralDetailsPanel"
148         showHeader={true}
149         title="SysLog Viewer">
150         <div className="section syslogViewerSection">
151           <label className="sectionLabel">Syslog Viewer URL</label>
152           <div className="syslogViewerControls">
153             <input className="textBox" type="text"
154               value={this.state.syslogViewerURL}
155               onBlur={this.handleUpdateSyslogViewerURL}
156               onChange={this.handleUpdateDetailTextField('syslogViewerURL')}
157               placeholder="syslogViewer URL"
158             />
159             <div className="goButton"
160               onClick={this.handleOpenSysLogViewerURL.bind(this)}>
161               <span>Go</span>
162               <span className="oi" data-glyph="arrow-right"
163                     title="Add event id" aria-hidden="true"></span>
164             </div>
165           </div>
166         </div>
167       </DashboardCard>
168     );
169   }
170 }
171 LoggingGeneralDetailsPanel.defaultProps = {
172   syslogViewerURL: ""
173 }
174
175
176
177 /**
178  *  Page level class renders the general logging config page of three panels:
179  * 1. Default severity per category
180  * 2. Events configuration
181  * 3. Syslog viewer setting, launch
182  */
183 export default class LoggingGeneral extends React.Component {
184
185   constructor(props) {
186     super(props);
187     this.state = LoggingStore.getState();
188     LoggingStore.listen(this.storeListener);
189     this.state.validateErrorEvent = 0;
190     this.state.validateErrorMsg = '';
191     this.state.isLoading = true;
192     this.state.showDumpStateButton = false;
193   }
194
195   storeListener = (state) => {
196     this.setState(state);
197   }
198
199   getData() {
200     LoggingStore.getLoggingConfig();
201     this.setState({
202       isLoading: _.isEmpty(this.state.loggingConfig)
203     });
204   }
205   componentWillUnmount = () => {
206     LoggingStore.unlisten(this.storeListener);
207   }
208
209   componentDidMount() {
210     //console.log("LoggingGeneral.componentDidMount called");
211     this.getData();
212   }
213
214   componentDidUpdate() {
215     //console.log("LoggingGeneral.componentDidUpdate called");
216   }
217
218   handleSave = (formData, e) => {
219     e.preventDefault();
220
221     if (this.validateData()) {
222       this.setState({
223         isLoading: true
224       });
225       LoggingStore.updateLoggingConfig(
226          this.collectNulledCategories(
227             this.state.initialLoggingConfig,
228             this.state.loggingConfig),
229          this.removeCategoryNulls(
230             this.state.loggingConfig
231           )
232      );
233     } else {
234       console.log("LoggingGeneral.handleSave failed validation");
235     }
236     this.context.router.push({pathname: ''});
237   }
238   removeCategoryNulls(config) {
239     let cleanConfig = _.cloneDeep(config);
240     let cleanSeverities = [];
241     config.defaultSeverities.map(function(d) {
242       if (d.severity) {
243         cleanSeverities.push(d);
244       }
245     });
246     cleanConfig.defaultSeverities = cleanSeverities;
247     return cleanConfig;
248   }
249   collectNulledCategories(oldCat, newCat) {
250     let nulledCategories = [];
251     let newSeverities = newCat.defaultSeverities;
252     let oldSeverities = oldCat.defaultSeverities;
253     newSeverities.map(function(c, i) {
254       if(!c.severity) {
255         if(oldSeverities[i].severity) {
256           //verify that categories are the same
257           if(oldSeverities[i].category == c.category) {
258             nulledCategories.push({category: c.category})
259           }
260         }
261       }
262     });
263     return nulledCategories;
264   }
265   validateData() {
266
267     function isEventIdValid(eventID) {
268       // Return true if null, empty string or a number, else return false
269       if (!eventID || eventID.length == 0) {
270         return true;
271       } else {
272         return (isNaN(+eventID)) ? false : true;
273       }
274     }
275
276     let invalidEventIDs = [];
277     this.state.loggingConfig.denyEventIDs.forEach(
278       function(eventID, index) {
279         if (!isEventIdValid(eventID)) {
280           invalidEventIDs.push({ eventID: eventID, index: index});
281         }
282       })
283     if (invalidEventIDs.length > 0) {
284       console.log("invalidEvents = ", invalidEventIDs);
285       if (invalidEventIDs.length == 1) {
286         let msg = 'There is ' + invalidEventIDs.length + ' invalid event ID';
287         this.validateError(msg);
288       } else {
289         let msg = 'There are ' + invalidEventIDs.length + ' invalid event IDs';
290         this.validateError(msg);
291       }
292       // How should we identify each invalid value?
293
294       return false;
295     } else {
296       return true;
297     }
298   }
299   handleCancel = (e) => {
300     console.log("LoggingGeneral.handleCancel clicked");
301     e.preventDefault();
302     // TODO: restore original state
303     LoggingStore.resetLoggingConfigData();
304     this.context.router.push({pathname: ''});
305
306   }
307
308   validateError = (msg) => {
309     this.setState({
310       validateErrorEvent: true,
311       validateErrorMsg: msg
312     });
313   }
314   validateReset = () => {
315     this.setState({
316       validateErrorEvent: false
317     });
318   }
319   returnCrouton = () => {
320     return <Crouton id={Date.now()}
321       message={this.state.validateErrorMsg}
322       type={'error'}
323       hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
324       onDismiss={this.validateReset}
325     />;
326   }
327   preventDefault = (e) => {
328     e.preventDefault();
329     e.stopPropagation();
330   }
331
332   renderDumpStateButton() {
333
334   }
335
336   render() {
337     var self = this;
338
339
340     let errorMessage = this.returnCrouton();
341
342     let syslogViewerURL = this.state.loggingConfig.syslogviewer;
343     let defaultSeverities = this.state.loggingConfig.defaultSeverities;
344     let severities = this.state.loggingConfig.severities;
345     let allowDuplicateEvents = this.state.loggingConfig.allowDuplicateEvents;
346     let denyEventIDs = this.state.loggingConfig.denyEventIDs;
347
348     let dumpStateButton = (
349         <button name="dump_state"
350             onClick={this.handleDumpState}>Dump State</button>);
351
352
353     return (
354       <div className="app-body">
355         {
356           (this.state.showLoggingNav) ? <LoggingNav currentPage="General" /> : ''
357         }
358
359         <form className="loggingPage loggingGeneral"
360           onSubmit={this.preventDefault}>
361           {errorMessage}
362           <ScreenLoader show={this.state.isLoading}/>
363           <div className="panelContainer">
364             <DefaultCategorySeverityPanel defaultSeverities={defaultSeverities}
365               severities={severities}
366               />
367             <LoggingEventsPanel allowDuplicateEvents={allowDuplicateEvents}
368               eventIDs={denyEventIDs} />
369             <LoggingGeneralDetailsPanel syslogViewerURL={syslogViewerURL} />
370           </div>
371           <div className="loggingPageFooter">
372             <div className="loggingformButtonGroup">
373               {
374                 (this.state.showDumpStateButton) ? dumpStateButton : ''
375               }
376               <Button className="cancel light" label="Reset"
377                 onClick={this.handleCancel} />
378               <Button className="save dark" role="button" label="Save"
379                 onClick={this.handleSave.bind(this, true)} />
380             </div>
381           </div>
382         </form>
383       </div>
384     );
385   }
386
387   // Dev and debug support
388   handleDumpState = (e) => {
389     console.log("State dump:");
390     console.log("event ids=", this.state.loggingConfig.denyEventIDs);
391     console.log("initial state=", this.state.initialLoggingConfig);
392     console.log("active state=", this.state.loggingConfig);
393   }
394 }
395 LoggingGeneral.contextTypes = {
396   router: React.PropTypes.object
397 }