3 * Copyright 2016 RIFT.IO Inc
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 import React from 'react';
19 import _ from 'lodash';
20 import './logging.scss';
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';
26 import LoggingActions from './loggingActions.js';
27 import LoggingStore from './loggingStore.js';
28 import LoggingNav from './loggingNav.jsx';
30 import CategorySeverityGrid from './categorySeverityGrid.jsx';
31 import DenyEventsEditGroup from './denyEventsEditGroup.jsx';
32 import {DropList, RadioButtonGroup, CardSection } from './loggingWidgets.jsx';
34 import Crouton from 'react-crouton';
35 import 'style/common.scss';
38 class DefaultCategorySeverityPanel extends React.Component {
41 const {defaultSeverities, severities, ...props } = this.props;
43 <DashboardCard className="defaultCategorySeverityPanel"
45 title="Default Category Severity">
46 <CategorySeverityGrid defaultSeverities={defaultSeverities}
47 severityOptions={severities}/>
54 class LoggingEventsPanel extends React.Component {
56 handleChangeAllowDuplicateEvents(e) {
57 console.log("LoggingEventsPanel.handleChangeAllowDuplicateEvents:");
58 console.log("- e.currentTarget.value=", e.currentTarget.value);
59 console.log("- e.currentTarget=", e.currentTarget);
61 // NOTE, we may want to generalize our string to boolean convert/compare
62 let allowFlag = (e.currentTarget.value == 'true');
63 LoggingStore.updateAllowDuplicateEvents(allowFlag);
66 handleAddDenyEvent(e) {
67 LoggingStore.addDenyEvent(null);
71 const {allowDuplicateEvents, eventIDs, radioItems, ...props} = this.props;
73 let selectedIndex = allowDuplicateEvents ? 0 :1;
76 <DashboardCard className="loggingEventsPanel"
79 <CardSection title={"Duplicates"}>
80 <div className="radioButtonGroupHeader">Allow duplicate events</div>
81 <RadioButtonGroup className="radioButtonGroup"
83 selectedItem={radioItems[selectedIndex]}
84 onChange={this.handleChangeAllowDuplicateEvents}
85 radioGroupName="allowDuplicateEvents"
88 <CardSection title={"Deny"}>
89 <DenyEventsEditGroup className="denyEventsEditGroup"
91 warnInvalidEventID={true}
93 <div className="plusButton" onClick={this.handleAddDenyEvent}>
94 <span className="oi" data-glyph="plus"
95 title="Add event id" aria-hidden="true"></span>
102 LoggingEventsPanel.defaultProps = {
104 { label: "Allow", value: true },
105 { label: "Deny", value: false }
107 allowDuplicateEvents: false,
114 class LoggingGeneralDetailsPanel extends React.Component {
118 syslogViewerURL: props.syslogViewerURL
122 componentWillReceiveProps(props) {
124 syslogViewerURL: props.syslogViewerURL
128 handleUpdateDetailTextField(fieldName) {
132 state[fieldName] = e.target.value;
133 self.setState(state);
137 handleUpdateSyslogViewerURL(e) {
138 LoggingStore.updateSyslogViewerURL(e.target.value);
140 handleOpenSysLogViewerURL(e) {
142 window.open(this.state.syslogViewerURL);
147 <DashboardCard className="loggingGeneralDetailsPanel"
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"
159 <div className="goButton"
160 onClick={this.handleOpenSysLogViewerURL.bind(this)}>
162 <span className="oi" data-glyph="arrow-right"
163 title="Add event id" aria-hidden="true"></span>
171 LoggingGeneralDetailsPanel.defaultProps = {
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
183 export default class LoggingGeneral extends React.Component {
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;
195 storeListener = (state) => {
196 this.setState(state);
200 LoggingStore.getLoggingConfig();
202 isLoading: _.isEmpty(this.state.loggingConfig)
205 componentWillUnmount = () => {
206 LoggingStore.unlisten(this.storeListener);
209 componentDidMount() {
210 //console.log("LoggingGeneral.componentDidMount called");
214 componentDidUpdate() {
215 //console.log("LoggingGeneral.componentDidUpdate called");
218 handleSave = (formData, e) => {
221 if (this.validateData()) {
225 LoggingStore.updateLoggingConfig(
226 this.collectNulledCategories(
227 this.state.initialLoggingConfig,
228 this.state.loggingConfig),
229 this.removeCategoryNulls(
230 this.state.loggingConfig
234 console.log("LoggingGeneral.handleSave failed validation");
236 this.context.router.push({pathname: ''});
238 removeCategoryNulls(config) {
239 let cleanConfig = _.cloneDeep(config);
240 let cleanSeverities = [];
241 config.defaultSeverities.map(function(d) {
243 cleanSeverities.push(d);
246 cleanConfig.defaultSeverities = cleanSeverities;
249 collectNulledCategories(oldCat, newCat) {
250 let nulledCategories = [];
251 let newSeverities = newCat.defaultSeverities;
252 let oldSeverities = oldCat.defaultSeverities;
253 newSeverities.map(function(c, i) {
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})
263 return nulledCategories;
267 function isEventIdValid(eventID) {
268 // Return true if null, empty string or a number, else return false
269 if (!eventID || eventID.length == 0) {
272 return (isNaN(+eventID)) ? false : true;
276 let invalidEventIDs = [];
277 this.state.loggingConfig.denyEventIDs.forEach(
278 function(eventID, index) {
279 if (!isEventIdValid(eventID)) {
280 invalidEventIDs.push({ eventID: eventID, index: index});
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);
289 let msg = 'There are ' + invalidEventIDs.length + ' invalid event IDs';
290 this.validateError(msg);
292 // How should we identify each invalid value?
299 handleCancel = (e) => {
300 console.log("LoggingGeneral.handleCancel clicked");
302 // TODO: restore original state
303 LoggingStore.resetLoggingConfigData();
304 this.context.router.push({pathname: ''});
308 validateError = (msg) => {
310 validateErrorEvent: true,
311 validateErrorMsg: msg
314 validateReset = () => {
316 validateErrorEvent: false
319 returnCrouton = () => {
320 return <Crouton id={Date.now()}
321 message={this.state.validateErrorMsg}
323 hidden={!(this.state.validateErrorEvent && this.state.validateErrorMsg)}
324 onDismiss={this.validateReset}
327 preventDefault = (e) => {
332 renderDumpStateButton() {
340 let errorMessage = this.returnCrouton();
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;
348 let dumpStateButton = (
349 <button name="dump_state"
350 onClick={this.handleDumpState}>Dump State</button>);
354 <div className="app-body">
356 (this.state.showLoggingNav) ? <LoggingNav currentPage="General" /> : ''
359 <form className="loggingPage loggingGeneral"
360 onSubmit={this.preventDefault}>
362 <ScreenLoader show={this.state.isLoading}/>
363 <div className="panelContainer">
364 <DefaultCategorySeverityPanel defaultSeverities={defaultSeverities}
365 severities={severities}
367 <LoggingEventsPanel allowDuplicateEvents={allowDuplicateEvents}
368 eventIDs={denyEventIDs} />
369 <LoggingGeneralDetailsPanel syslogViewerURL={syslogViewerURL} />
371 <div className="loggingPageFooter">
372 <div className="loggingformButtonGroup">
374 (this.state.showDumpStateButton) ? dumpStateButton : ''
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)} />
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);
395 LoggingGeneral.contextTypes = {
396 router: React.PropTypes.object