Skip to content

Commit

Permalink
feat(jobs): Add disable/enable scheduled jobs to UI (#241)
Browse files Browse the repository at this point in the history
feat(jobs): Add disable/enable scheduled jobs to UI
  • Loading branch information
manorlh authored and NivLipetz committed Dec 9, 2019
1 parent df7fbc1 commit 2adf634
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 50 deletions.
6 changes: 3 additions & 3 deletions ui/src/components/UiSwitcher/UiSwitcher.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ input[type="checkbox"].switch + div {
}

.onColor {
background-color: #3b89ec;
background-color: #a3a8a5;
}

.offColor {
background-color: #d66969;
background-color: #a3a8a5;
}

input[type="checkbox"].switch:checked + div {
background-position: 0 0;
}

input[type="checkbox"].switch:checked + div {
background-color: #57BB00;
background-color: #557EFF;
}

input[type="checkbox"].switch + div > div {
Expand Down
106 changes: 74 additions & 32 deletions ui/src/features/configurationColumn.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@ 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,faRedo, faRunning, 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";
import {v4 as uuid} from "uuid";
import TooltipWrapper from '../components/TooltipWrapper';
import {getTimeFromCronExpr} from './utils';
import UiSwitcher from '../components/UiSwitcher';


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

const columns = [
{
Expand All @@ -25,7 +34,7 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
</TableHeader>
),
accessor: 'report_id'
}, {
}, {
id: 'name',
Header: () => (
<TableHeader sortable={false}>
Expand All @@ -34,7 +43,7 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: 'name',
headerClassName: css['header-name'],
className:css['header-name']
className: css['header-name']
},
{
id: 'description',
Expand Down Expand Up @@ -75,22 +84,23 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
Edit
</TableHeader>
),
accessor: data => data.type ==='basic' ? <ViewButton icon={faPen} onClick={(e) => {
e.stopPropagation();
onEdit(data)}}/> :
accessor: data => data.type === 'basic' ? <ViewButton icon={faPen} onClick={(e) => {
e.stopPropagation();
onEdit(data)
}}/> :
<TooltipWrapper
content={
<div>
DSL not supported
</div>}
dataId={`tooltipKey`}
place='top'
offset={{top: 1}}
>
<div data-tip data-for={`tooltipKey_na`} style={{cursor: 'pointer', width:'18px'}}>
N/A
</div>
</TooltipWrapper> ,
<div>
DSL not supported
</div>}
dataId={`tooltipKey`}
place='top'
offset={{top: 1}}
>
<div data-tip data-for={`tooltipKey_na`} style={{cursor: 'pointer', width: '18px'}}>
N/A
</div>
</TooltipWrapper>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -103,7 +113,7 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: 'test_name',
headerClassName: css['header-name'],
className:css['header-name']
className: css['header-name']

},
{
Expand Down Expand Up @@ -201,7 +211,7 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
</TableHeader>
),
accessor: 'last_run',
minWidth:150
minWidth: 150
},

{
Expand Down Expand Up @@ -249,7 +259,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton onClick={(e) => {
e.stopPropagation();
onReportView(data)}}/>,
onReportView(data)
}}/>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -262,7 +273,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton onClick={(e) => {
e.stopPropagation();
window.open(data.grafana_report, '_blank')}}/>,
window.open(data.grafana_report, '_blank')
}}/>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -275,7 +287,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton icon={faEye} onClick={(e) => {
e.stopPropagation();
onRawView(data)}}/>,
onRawView(data)
}}/>,
className: css['small-header'],
headerClassName: css['small-header'],

Expand All @@ -289,7 +302,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton icon={faRedo} onClick={(e) => {
e.stopPropagation();
onRunTest(data)}}/>,
onRunTest(data)
}}/>,
className: css['small-header'],
headerClassName: css['small-header'],

Expand All @@ -303,7 +317,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton icon={faRunning} onClick={(e) => {
e.stopPropagation();
onRunTest(data)}}/>,
onRunTest(data)
}}/>,
className: css['small-header'],
headerClassName: css['small-header'],

Expand All @@ -317,7 +332,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton icon={faTrashAlt} onClick={(e) => {
e.stopPropagation();
onDelete(data)}}/>,
onDelete(data)
}}/>,
className: css['small-header'],
headerClassName: css['small-header']
},
Expand All @@ -330,7 +346,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
),
accessor: data => <ViewButton text={'Run'} onClick={(e) => {
e.stopPropagation();
onRunTest(data)}}/>,
onRunTest(data)
}}/>,
className: css['small-header'],
headerClassName: css['small-header']
}, {
Expand All @@ -343,7 +360,8 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
accessor: data => (<ViewButton icon={faCloudDownloadAlt}
onClick={(e) => {
e.stopPropagation();
window.open(`${env.PREDATOR_URL}/jobs/${data.job_id}/runs/${data.report_id}/logs`, '_blank')}}/>),
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 @@ -357,7 +375,32 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
const disabled = (data.status !== 'in_progress' && data.status !== 'started');
return (<ViewButton disabled={disabled} icon={faStopCircle} onClick={(e) => {
e.stopPropagation();
onStop(data)}}/>)
onStop(data)
}}/>)
},
className: css['small-header'],
headerClassName: css['small-header']

},
{
id: 'enabled_disabled',
Header: () => (
<TableHeader sortable={false}>
Enabled
</TableHeader>
),
accessor: (data) => {
const activated = (typeof data.enabled === 'undefined' ? true : data.enabled);
return (
<UiSwitcher
onChange={(value) => {
onEnableDisable(data,value)
}}
disabledInp={false}
activeState={activated}
height={12}
width={22}
/>)
},
className: css['small-header'],
headerClassName: css['small-header']
Expand All @@ -369,15 +412,14 @@ export const getColumns = ({columnsNames, sortHeader = '', onSort, onReportView,
// return filter(columns, (column) => columnsNames.includes(column.id))
return columnsNames.map((name) => {
const column = columns.find((c) => c.id === name);
if(!column){
if (!column) {
throw new Error(`column ${name} not found`);
}
return column;
});
};



const dateFormatter = (cell, row) => {
const timePattern = 'DD-MM-YYYY hh:mm:ss a';

Expand All @@ -390,7 +432,7 @@ const dateFormatter = (cell, row) => {
}
};

const ViewButton = ({onClick, icon, disabled,text}) => {
const ViewButton = ({onClick, icon, disabled, text}) => {

const element = icon ? <FontAwesomeIcon
className={classnames(css['icon'], {[css['action-style']]: !disabled, [css['disabled-button']]: disabled})}
Expand Down
1 change: 1 addition & 0 deletions ui/src/features/configurationColumn.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
}

.small-header {
margin:auto;
width:50px !important;
}

Expand Down
36 changes: 27 additions & 9 deletions ui/src/features/get-jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
processingDeleteJob,
deleteJobSuccess,
getJobsWithTestNameAndLastRun,
createJobSuccess
createJobSuccess,
editJobSuccess,
errorOnJobAction
} from './redux/selectors/jobsSelector';
import { tests } from './redux/selectors/testsSelector';
import { reports } from './redux/selectors/reportsSelector';
Expand All @@ -26,7 +28,7 @@ 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', 'run_now', 'raw', 'delete'];
const columnsNames = ['test_name', 'environment', 'duration', 'arrival_rate', 'ramp_to', 'parallelism', 'max_virtual_users', 'cron_expression', 'last_run', 'run_now', 'raw', 'delete', 'enabled_disabled'];
const DESCRIPTION = 'Scheduled jobs configured with a cron expression.';

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

Expand Down Expand Up @@ -76,7 +79,13 @@ class getJobs extends React.Component {
delete request.cron_expression;
request.run_immediately = true;
this.props.createJob(request);
this.setState({rerunJob:job});
this.setState({rerunJob: job});
};

onEnableDisable = (data, value) => {
const request = {enabled: value};
this.setState({editJob:data});
this.props.editJob(data.id, request);
};

submitDelete = () => {
Expand Down Expand Up @@ -138,7 +147,8 @@ class getJobs extends React.Component {
onSort: this.onSort,
onRawView: this.onRawView,
onRunTest: this.onRunTest,
onDelete: this.onDelete
onDelete: this.onDelete,
onEnableDisable: this.onEnableDisable,
});
const feedbackMessage = this.generateFeedbackMessage();
return (
Expand Down Expand Up @@ -167,17 +177,19 @@ class getJobs extends React.Component {
onSubmit={this.submitDelete} errorOnDelete={this.props.deleteError}
onCancel={this.cancelDelete} /> : null}
{feedbackMessage && <Snackbar
open={!!(this.props.deleteJobSuccess || this.props.jobSuccess)}
open={!!(this.props.deleteJobSuccess || this.props.jobSuccess || this.props.editJobSuccess)}
bodyStyle={{ backgroundColor: '#2fbb67' }}
message={feedbackMessage}
autoHideDuration={4000}
onRequestClose={() => {
this.props.getAllJobs();
this.props.clearDeleteJobSuccess();
this.props.createJobSuccess(undefined);
this.props.setEditJobSuccess(undefined);
this.setState({
jobToDelete: undefined,
rerunJob: null
rerunJob: null,
editJob: null,
});
}}
/>}
Expand All @@ -192,6 +204,9 @@ class getJobs extends React.Component {
if(this.props.jobSuccess && this.state.rerunJob){
return `Job created successfully: ${this.props.jobSuccess.id}`;
}
if(this.props.editJobSuccess && this.state.editJob){
return `Job edited successfully: ${this.state.editJob.id}`;
}

}
}
Expand All @@ -206,8 +221,9 @@ function mapStateToProps(state) {
deleteJobSuccess: deleteJobSuccess(state),
tests: tests(state),
reports: reports(state),
jobSuccess: createJobSuccess(state)

jobSuccess: createJobSuccess(state),
editJobSuccess: editJobSuccess(state),
errorOnJobAction:errorOnJobAction(state)
};
}

Expand All @@ -217,12 +233,14 @@ const mapDispatchToProps = {
getAllJobs: Actions.getJobs,
getJob: Actions.getJob,
createJob: Actions.createJob,
editJob: Actions.editJob,
deleteJob: Actions.deleteJob,
clearDeleteJobSuccess: Actions.clearDeleteJobSuccess,
clearErrorOnDeleteJob: Actions.clearErrorOnDeleteJob,
getTests: Actions.getTests,
getAllReports: Actions.getLastReports,
createJobSuccess: Actions.createJobSuccess,
setEditJobSuccess: Actions.editJobSuccess,
};

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

0 comments on commit 2adf634

Please sign in to comment.