Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Commit

Permalink
Store and reuse commands, fixes #22
Browse files Browse the repository at this point in the history
  • Loading branch information
martinhoefling committed Jan 10, 2016
1 parent 37adc19 commit 59f3f90
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 32 deletions.
3 changes: 3 additions & 0 deletions app/ActionCreators.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ var getDocumentationSuccess = (docType, documentation) =>
export const storeCommand = (timestamp, clientConfig, targetConfig, functionConfig) =>
({ type: Constants.STORE_COMMAND, timestamp, clientConfig, targetConfig, functionConfig });

export const clearCommands = () => ({ type: Constants.CLEAR_COMMANDS });
export const clearCommand = (command) => ({ type: Constants.CLEAR_COMMAND, command });

export const setSetting = (setting, value) => ({ type: Constants.SET_SETTING, setting, value });
export const clearSetting = setting => ({ type: Constants.CLEAR_SETTING, setting });

Expand Down
2 changes: 2 additions & 0 deletions app/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export default {
CLEAR_EVENTS: 'CLEAR_EVENTS',

STORE_COMMAND: 'STORE_COMMAND',
CLEAR_COMMANDS: 'CLEAR_COMMANDS',
CLEAR_COMMAND: 'CLEAR_COMMAND',

DOCUMENTATION_TYPE: {
EXECUTE: 'execution',
Expand Down
8 changes: 7 additions & 1 deletion app/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ import DetailedJobTab from 'containers/tabs/DetailedJobTab';
import MinionsTab from 'containers/tabs/MinionsTab';
import EventsTab from 'containers/tabs/EventsTab';
import SettingsTab from 'containers/tabs/SettingsTab';
import ExecuteHistoryTab from 'containers/tabs/ExecuteHistoryTab';
import ExecuteCommandTab from 'containers/tabs/ExecuteCommandTab';

import Constants from 'Constants';

const Routes = (
<Route path=''>
<Route component={Main} path={Constants.URL.ROOT}>
<Route component={Login} path='login' />
<Route component={ExecuteTab} path='execute' />
<Route component={ExecuteTab} path='execute'>
<Route component={ExecuteCommandTab} path='command' />
<Route component={ExecuteHistoryTab} path='history' />
</Route>
<Route component={JobsTab} path='job' />
<Route component={DetailedJobTab} path='job/:jid' />
<Route component={MinionsTab} path='minion' />
Expand Down
9 changes: 2 additions & 7 deletions app/components/TabHeaders.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ const TabHeaders = React.createClass({
location: React.PropTypes.object.isRequired
},

getInitialState() {
return {
activeTab: 'execution'
};
},

componentWillMount() {
if (!this.props.currentSession) {
this.props.testSessionStatus();
Expand All @@ -51,7 +45,8 @@ const TabHeaders = React.createClass({
}, this);

var path = this.props.location.pathname.substring(config.APP_BASE_URL.length + 1),
index = Math.max(0, _.findIndex(TABS, tab => path.indexOf(tab.toLowerCase()) === 0));
tabstr = path.split('/')[0],
index = Math.max(0, _.findIndex(TABS, tab => tabstr.indexOf(tab.toLowerCase()) === 0));

return (
<Tabs initialSelectedIndex={index}>
Expand Down
14 changes: 5 additions & 9 deletions app/components/execute/ExecutedCommand.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ import styles from './ExecutedCommand.less';
const ExecutedCommand = React.createClass({
propTypes: {
command: React.PropTypes.object.isRequired,
clients: React.PropTypes.array.isRequired
},

onLoadCommand() {
},

onRemoveCommand() {
clients: React.PropTypes.array.isRequired,
onLoadCommand: React.PropTypes.func.isRequired,
onRemoveCommand: React.PropTypes.func.isRequired
},

renderFunction() {
Expand Down Expand Up @@ -120,14 +116,14 @@ const ExecutedCommand = React.createClass({
<RaisedButton
label='Load Command'
primary={true}
onClick={this.onLoadCommand}
onClick={() => this.props.onLoadCommand(this.props.command)}
/>
</div>
<div className={styles.button}>
<RaisedButton
label='Remove Command'
primary={true}
onClick={this.onRemoveCommand}
onClick={() => this.props.onRemoveCommand(this.props.command)}
/>
</div>
</div>
Expand Down
46 changes: 43 additions & 3 deletions app/containers/tabs/ExecuteHistoryTab.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
import React from 'react';
import { connect } from 'react-redux';
import { pushState } from 'redux-router';

import RaisedButton from 'material-ui/lib/raised-button';

import ExecutedCommand from 'components/execute/ExecutedCommand';
import { getClients } from 'models/Clients';
import { clearCommand, clearCommands,
setClientConfiguration, setTargetConfiguration, setFunctionConfiguration } from 'ActionCreators';
import Constants from 'Constants';

import styles from './ExecuteHistoryTab.less';

const ExecuteHistoryTab = React.createClass({
propTypes: {
commandHistory: React.PropTypes.array.isRequired,
clients: React.PropTypes.array
clients: React.PropTypes.array,
clearCommand: React.PropTypes.func.isRequired,
clearCommands: React.PropTypes.func.isRequired,
setClientConfiguration: React.PropTypes.func.isRequired,
setTargetConfiguration: React.PropTypes.func.isRequired,
setFunctionConfiguration: React.PropTypes.func.isRequired,
pushState: React.PropTypes.func.isRequired
},

loadCommand(command) {
this.props.setClientConfiguration(command.clientConfig);
this.props.setTargetConfiguration(command.targetConfig);
this.props.setFunctionConfiguration(command.functionConfig);
this.props.pushState(null, Constants.URL.ROOT + 'execute/command');
},

removeCommand(command) {
this.props.clearCommand(command);
},

render() {
Expand All @@ -16,8 +41,22 @@ const ExecuteHistoryTab = React.createClass({
}
return (
<div>
<div className={styles.button}>
<RaisedButton
label='Clear Command History'
primary={true}
onClick={() => this.props.clearCommands()}
/>
</div>
{this.props.commandHistory.map(command =>
<ExecutedCommand key={JSON.stringify(command)} command={command} clients={this.props.clients}/>)}
<ExecutedCommand
key={JSON.stringify(command)}
command={command}
clients={this.props.clients}
onLoadCommand={this.loadCommand}
onRemoveCommand={this.removeCommand}
/>
)}
</div>
);
}
Expand All @@ -32,4 +71,5 @@ function select(state) {
};
}

export default connect(select)(ExecuteHistoryTab);
export default connect(select, { clearCommand, clearCommands, pushState,
setClientConfiguration, setTargetConfiguration, setFunctionConfiguration })(ExecuteHistoryTab);
3 changes: 3 additions & 0 deletions app/containers/tabs/ExecuteHistoryTab.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:local(.button) {
margin: 10px;
}
57 changes: 46 additions & 11 deletions app/containers/tabs/ExecuteTab.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,61 @@
import React from 'react';
import { connect } from 'react-redux';
import { pushState } from 'redux-router';

import Tab from 'material-ui/lib/tabs/tab';
import Tabs from 'material-ui/lib/tabs/tabs';

import ExecuteHistoryTab from 'containers/tabs/ExecuteHistoryTab';
import ExecuteCommandTab from 'containers/tabs/ExecuteCommandTab';
import Constants from 'Constants';
import config from 'config';

import tabStyle from './Tab.less';

const TABS = ['command', 'history'];

const ExecuteTab = React.createClass({
propTypes: {
pushState: React.PropTypes.func.isRequired,
location: React.PropTypes.object.isRequired
},

render() {
renderTabs() {
var path = this.props.location.pathname.substring(config.APP_BASE_URL.length + 1),
tabstr = path.split('/')[1],
index = Math.max(0, _.findIndex(TABS, tab => tabstr.indexOf(tab.toLowerCase()) === 0));
return (
<Tabs className={tabStyle.this}>
<Tab label='Execute Job' >
<ExecuteCommandTab/>
</Tab>
<Tab label='Execution History' >
<ExecuteHistoryTab/>
</Tab>
<Tabs initialSelectedIndex={index}>
<Tab
route='command'
onActive={this._onActive}
label='Execute Job'
/>
<Tab
route='history'
onActive={this._onActive}
label='Execution History'
/>
</Tabs>
);
},

_onActive(tab) {
this.props.pushState(null, Constants.URL.ROOT + 'execute/' + tab.props.route);
},

render() {
return (
<div className={tabStyle.this}>
{this.renderTabs()}
{this.props.children}
</div>
);
}
});

export default ExecuteTab;
function select(state) {
return {
location: state.router.location
};
}

export default connect(select, { pushState })(ExecuteTab);
11 changes: 10 additions & 1 deletion app/reducers/CommandHistoryReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import _ from 'lodash';
import Constants from 'Constants';

const initialState = [];
var index;

export default function commandHistoryReducer(state = initialState, action) {
switch (action.type) {
case Constants.STORE_COMMAND:
var index = state.findIndex(function (storedCommand) {
index = state.findIndex(function (storedCommand) {
var stored = _.omit(storedCommand, ['timestamps']);
var command = _.pick(action, ['clientConfig', 'targetConfig', 'functionConfig']);
return _.isEqual(stored, command);
Expand All @@ -23,6 +24,14 @@ export default function commandHistoryReducer(state = initialState, action) {
var timestamps = [...newState[index].timestamps.slice(), action.timestamp];
newState[index] = Object.assign({}, newState[index], { timestamps });
return newState;
case Constants.CLEAR_COMMANDS:
return [];
case Constants.CLEAR_COMMAND:
index = state.indexOf(action.command);
if (index > -1) {
return [...state.slice(0, index), ...state.slice(index + 1)];
}
return state;
default:
return state;
}
Expand Down
43 changes: 43 additions & 0 deletions tests/app/reducers/CommandHistoryReducer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,42 @@ describe('CommandHistoryReducer', function () {
expect(state.length).toEqual(2);
});

it('Clears a specific command commands', function () {
var action1 = createDefaultAction();
var action2 = createStoreAction({
batch: '10',
client: 'local_batch'
}, {}, {});
var action3 = createStoreAction({
batch: '20',
client: 'local_batch'
}, {}, {});
var state = commandHistoryReducer(undefined, action1);
state = commandHistoryReducer(state, action2);
state = commandHistoryReducer(state, action3);
var remainingCommands = [state[0], state[2]];
state = commandHistoryReducer(state, { type: Constants.CLEAR_COMMAND, command: state[1] });
expect(state).toEqual(remainingCommands);
});

it('Clears no command if specified command is invalid', function () {
var action1 = createDefaultAction();
var action2 = createStoreAction({
batch: '10',
client: 'local_batch'
}, {}, {});
var action3 = createStoreAction({
batch: '20',
client: 'local_batch'
}, {}, {});
var state = commandHistoryReducer(undefined, action1);
state = commandHistoryReducer(state, action2);
state = commandHistoryReducer(state, action3);
var remainingCommands = [state[0], state[1], state[2]];
state = commandHistoryReducer(state, { type: Constants.CLEAR_COMMAND, command: { invalid: 'invalid' } });
expect(state).toEqual(remainingCommands);
});

it('Only appends timestamps for equal commands', function () {
var action = createDefaultAction();
var anotherAction = createDefaultAction();
Expand All @@ -72,4 +108,11 @@ describe('CommandHistoryReducer', function () {
expect(state[0].timestamps[0]).toEqual(action.timestamp);
expect(state[0].timestamps[1]).toEqual(anotherAction.timestamp);
});

it('Clears all commands', function () {
var action = createDefaultAction();
var state = commandHistoryReducer(undefined, action);
state = commandHistoryReducer(state, { type: Constants.CLEAR_COMMANDS });
expect(state.length).toEqual(0);
});
});

0 comments on commit 59f3f90

Please sign in to comment.