Skip to content

Commit

Permalink
feat: rerun feature, search by enter (#137)
Browse files Browse the repository at this point in the history
* feat: rerun feature,search-enter
  • Loading branch information
manorlh authored and enudler committed May 16, 2019
1 parent 79bbd24 commit e340979
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 92 deletions.
8 changes: 7 additions & 1 deletion ui/src/components/SearchBar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ class SearchBar extends React.Component {
<div className={style.wrapper}>
<div className={style['title-wrapper']}>
<TitleInput title={'Search'}>
<Input className={style.input} onChange={(event) => this.setState({value: event.target.value})}/>
<Input className={style.input} onKeyDown={this.handleEnter} onChange={(event) => this.setState({value: event.target.value})}/>
</TitleInput>
</div>
<Button hover className={style['button']} onClick={()=>{onSearch(this.state.value)}}>Search</Button>
</div>
)
}

handleEnter=(e)=>{
if (e.key === 'Enter') {
this.props.onSearch(this.state.value)
}
}
}

export default SearchBar;
28 changes: 8 additions & 20 deletions ui/src/features/PerformanceUI/components/JobForm/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import TextArea from '../../../../components/TextArea';
import MultiValueInput from '../../../../components/MultiValueInput';
import UiSwitcher from '../../../../components/UiSwitcher';
import {filter} from 'lodash';
import {createJobRequest} from '../../instance/requestBuilder';

const DESCRIPTION = 'Predator executes tests through jobs. Use this form to specify the parameters for the job you want to execute.';
const inputTypes = {
Expand Down Expand Up @@ -228,7 +229,7 @@ class Form extends React.Component {
}

render() {
const {closeDialog, processingAction} = this.props;
const {closeDialog, processingAction, serverError,clearErrorOnCreateJob} = this.props;
return (
<Modal width={'50%'} onExit={closeDialog}>
<FormWrapper title={'Create a new job'} description={DESCRIPTION}>
Expand All @@ -248,7 +249,10 @@ class Form extends React.Component {
<Button spinner={processingAction} hover disabled={!!this.isThereErrorOnForm()}
onClick={this.whenSubmit}>Submit</Button>
</div>
{this.props.serverError ? <ErrorDialog showMessage={this.props.serverError}/> : null}
{ serverError &&
<ErrorDialog closeDialog={() => {clearErrorOnCreateJob()}} showMessage={serverError}/>
}

</div>
</FormWrapper>
</Modal>
Expand Down Expand Up @@ -300,7 +304,6 @@ class Form extends React.Component {
</TitleInput>
);
default:
console.log('oneItem', oneItem)
return (
<div>
<TitleInput key={oneItem.key} title={oneItem.floatingLabelText}
Expand All @@ -318,25 +321,10 @@ class Form extends React.Component {
}

whenSubmit = () => {

let body = {
this.props.createJob(createJobRequest(Object.assign({}, this.state, {
test_id: this.props.data.id,
arrival_rate: parseInt(this.state.arrival_rate),
duration: parseInt(this.state.duration) * 60,
ramp_to: this.state.ramp_to ? parseInt(this.state.ramp_to) : undefined,
environment: this.state.environment,
run_immediately: (this.state.run_immediately === undefined) ? false : this.state.run_immediately,
emails: this.state.emails,
webhooks: this.state.webhooks,
notes: this.state.notes,
parallelism: this.state.parallelism ? parseInt(this.state.parallelism) : undefined,
max_virtual_users: this.state.max_virtual_users ? parseInt(this.state.max_virtual_users) : undefined
};
if (this.state.cron_expression) {//should exist and not empty
body.cron_expression = this.state.cron_expression
}
body = JSON.parse(JSON.stringify(body));
this.props.createJob(body);
})));
};
}

Expand Down
64 changes: 54 additions & 10 deletions ui/src/features/PerformanceUI/instance/configurationColumn.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Moment from 'moment';
import prettySeconds from 'pretty-seconds';
import 'font-awesome/css/font-awesome.min.css';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faEye, faCloudDownloadAlt, faStopCircle, faTrashAlt, faPen} from '@fortawesome/free-solid-svg-icons'
import {faEye,faRedo, faRunning, faCloudDownloadAlt, faStopCircle, faTrashAlt, faPen} from '@fortawesome/free-solid-svg-icons'
import classnames from 'classnames';
import css from './configurationColumn.scss';
import env from "../../../App/common/env";
Expand All @@ -14,7 +14,7 @@ import TooltipWrapper from '../../../components/TooltipWrapper';
import {getTimeFromCronExpr} from './utils';


export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView, onRawView,onStop, onDelete, onEdit, onRunTest }) => {
export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView, onRawView, onStop, onDelete, onEdit, onRunTest}) => {

const columns = [
{
Expand Down Expand Up @@ -69,7 +69,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Edit
</TableHeader>
),
accessor: data => data.type ==='basic' ? <ViewButton icon={faPen} onClick={() => onEdit(data)}/> : 'N/A',
accessor: data => data.type ==='basic' ? <ViewButton icon={faPen} onClick={(e) => {
e.stopPropagation();
onEdit(data)}}/> : 'N/A',
className: css['small-header'],
headerClassName: css['small-header']
},
Expand Down Expand Up @@ -226,7 +228,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Report
</TableHeader>
),
accessor: data => <ViewButton onClick={() => onReportView(data)}/>,
accessor: data => <ViewButton onClick={(e) => {
e.stopPropagation();
onReportView(data)}}/>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -237,7 +241,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Grafana
</TableHeader>
),
accessor: data => <ViewButton onClick={() => window.open(data.grafana_report, '_blank')}/>,
accessor: data => <ViewButton onClick={(e) => {
e.stopPropagation();
window.open(data.grafana_report, '_blank')}}/>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -248,7 +254,37 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Raw
</TableHeader>
),
accessor: data => <ViewButton icon={faEye} onClick={() => onRawView(data)}/>,
accessor: data => <ViewButton icon={faEye} onClick={(e) => {
e.stopPropagation();
onRawView(data)}}/>,
className: css['small-header'],
headerClassName: css['small-header'],

},
{
id: 'rerun',
Header: () => (
<TableHeader sortable={false}>
Rerun
</TableHeader>
),
accessor: data => <ViewButton icon={faRedo} onClick={(e) => {
e.stopPropagation();
onRunTest(data)}}/>,
className: css['small-header'],
headerClassName: css['small-header'],

},
{
id: 'run_now',
Header: () => (
<TableHeader sortable={false}>
Run Now
</TableHeader>
),
accessor: data => <ViewButton icon={faRunning} onClick={(e) => {
e.stopPropagation();
onRunTest(data)}}/>,
className: css['small-header'],
headerClassName: css['small-header'],

Expand All @@ -260,7 +296,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Delete
</TableHeader>
),
accessor: data => <ViewButton icon={faTrashAlt} onClick={() => onDelete(data)}/>,
accessor: data => <ViewButton icon={faTrashAlt} onClick={(e) => {
e.stopPropagation();
onDelete(data)}}/>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -271,7 +309,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Run Test
</TableHeader>
),
accessor: data => <ViewButton text={'Run'} onClick={() => onRunTest(data)}/>,
accessor: data => <ViewButton text={'Run'} onClick={(e) => {
e.stopPropagation();
onRunTest(data)}}/>,
className: css['small-header'],
headerClassName: css['small-header']
}, {
Expand All @@ -282,7 +322,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
</TableHeader>
),
accessor: data => (<ViewButton icon={faCloudDownloadAlt}
onClick={() => window.open(`${env.PREDATOR_URL}/jobs/${data.job_id}/runs/${data.report_id}/logs`, '_blank')}/>),
onClick={(e) => {
e.stopPropagation();
window.open(`${env.PREDATOR_URL}/jobs/${data.job_id}/runs/${data.report_id}/logs`, '_blank')}}/>),
className: css['small-header'],
headerClassName: css['small-header']
}, {
Expand All @@ -294,7 +336,9 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: (data) => {
const disabled = (data.status !== 'in_progress' && data.status !== 'started');
return (<ViewButton disabled={disabled} icon={faStopCircle} onClick={() => onStop(data)}/>)
return (<ViewButton disabled={disabled} icon={faStopCircle} onClick={(e) => {
e.stopPropagation();
onStop(data)}}/>)
},
className: css['small-header'],
headerClassName: css['small-header']
Expand Down
59 changes: 40 additions & 19 deletions ui/src/features/PerformanceUI/instance/get-jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import {
job,
processingGetJobs,
errorOnGetJobs,
errorOnGetJob,
processingDeleteJob,
deleteJobSuccess,
getJobsWithTestNameAndLastRun
getJobsWithTestNameAndLastRun,
createJobSuccess
} from './redux/selectors/jobsSelector';
import { tests } from './redux/selectors/testsSelector';
import { reports } from './redux/selectors/reportsSelector';
Expand All @@ -21,11 +21,12 @@ import Page from '../../../components/Page';
import { ReactTableComponent } from '../../../components/ReactTable';
import { getColumns } from './configurationColumn';
import _ from 'lodash';
import {createJobRequest} from './requestBuilder';

const noDataMsg = 'There is no data to display.';
const errorMsgGetTests = 'Error occurred while trying to get all jobs.';
const REFRESH_DATA_INTERVAL = 30000;
const columnsNames = ['test_name', 'environment', 'duration', 'arrival_rate', 'ramp_to', 'parallelism', 'max_virtual_users', 'cron_expression', 'last_run', 'raw', 'delete'];
const columnsNames = ['test_name', 'environment', 'duration', 'arrival_rate', 'ramp_to', 'parallelism', 'max_virtual_users', 'cron_expression', 'last_run', 'run_now', 'raw', 'delete'];
const DESCRIPTION = 'Scheduled jobs configured with a cron expression.';

class getJobs extends React.Component {
Expand All @@ -37,7 +38,8 @@ class getJobs extends React.Component {
deleteDialog: false,
openViewJob: false,
jobToDelete: undefined,
sortedJobs: []
sortedJobs: [],
rerunJob: null
};
}

Expand Down Expand Up @@ -69,6 +71,14 @@ class getJobs extends React.Component {
this.setState({ openViewJob: job });
};

onRunTest = (job) => {
const request = createJobRequest(job);
delete request.cron_expression;
request.run_immediately = true;
this.props.createJob(request);
this.setState({rerunJob:job});
};

submitDelete = () => {
this.props.deleteJob(this.state.jobToDelete.id);
this.props.getAllJobs();
Expand Down Expand Up @@ -127,9 +137,10 @@ class getJobs extends React.Component {
columnsNames,
onSort: this.onSort,
onRawView: this.onRawView,
onRunTest: this.onRunTest,
onDelete: this.onDelete
});

const feedbackMessage = this.generateFeedbackMessage();
return (
<Page title={'Scheduled Jobs'} description={DESCRIPTION}>
<ReactTableComponent
Expand All @@ -138,7 +149,7 @@ class getJobs extends React.Component {
rowHeight={'46px'}
manual={false}
data={sortedJobs}
pageSize={10}
pageSize={10}
columns={columns}
noDataText={noDataText}
showPagination
Expand All @@ -155,27 +166,34 @@ class getJobs extends React.Component {
display={this.state.jobToDelete ? `job ${this.state.jobToDelete.id}` : ''}
onSubmit={this.submitDelete} errorOnDelete={this.props.deleteError}
onCancel={this.cancelDelete} /> : null}
{/* //TODO seems deleteError will not work */}
<Snackbar
anchorOrigin={{
vertical: 'top',
horizontal: 'center'
}}
open={this.props.deleteJobSuccess}
{feedbackMessage && <Snackbar
open={!!(this.props.deleteJobSuccess || this.props.jobSuccess)}
bodyStyle={{ backgroundColor: '#2fbb67' }}
message={(this.state.jobToDelete && this.state.jobToDelete.id) ? `Job deleted successfully: ${this.state.jobToDelete.id}` : ''}
message={feedbackMessage}
autoHideDuration={4000}
onRequestClose={() => {
this.props.getAllJobs();
this.props.clearDeleteJobSuccess();
this.props.createJobSuccess(undefined);
this.setState({
jobToDelete: undefined
jobToDelete: undefined,
rerunJob: null
});
}}
/>
/>}
</Page>
);
}

generateFeedbackMessage = ()=>{
if(this.state.jobToDelete && this.state.jobToDelete.id){
return `Job deleted successfully: ${this.state.jobToDelete.id}`
}
if(this.props.jobSuccess && this.state.rerunJob){
return `Job created successfully: ${this.props.jobSuccess.id}`;
}

}
}

function mapStateToProps(state) {
Expand All @@ -184,11 +202,12 @@ function mapStateToProps(state) {
job: job(state),
processingGetJobs: processingGetJobs(state),
errorOnGetJobs: errorOnGetJobs(state),
errorOnGetJob: errorOnGetJob(state),
processingDeleteJob: processingDeleteJob(state),
deleteJobSuccess: deleteJobSuccess(state),
tests: tests(state),
reports: reports(state)
reports: reports(state),
jobSuccess: createJobSuccess(state)

};
}

Expand All @@ -197,11 +216,13 @@ const mapDispatchToProps = {
clearErrorOnGetJobs: Actions.clearErrorOnGetJobs,
getAllJobs: Actions.getJobs,
getJob: Actions.getJob,
createJob: Actions.createJob,
deleteJob: Actions.deleteJob,
clearDeleteJobSuccess: Actions.clearDeleteJobSuccess,
clearErrorOnDeleteJob: Actions.clearErrorOnDeleteJob,
getTests: Actions.getTests,
getAllReports: Actions.getLastReports
getAllReports: Actions.getLastReports,
createJobSuccess: Actions.createJobSuccess,
};

export default connect(mapStateToProps, mapDispatchToProps)(getJobs);

0 comments on commit e340979

Please sign in to comment.