diff --git a/ui/src/app.js b/ui/src/app.js index a34de62e3a..0f35d62615 100644 --- a/ui/src/app.js +++ b/ui/src/app.js @@ -1,27 +1,27 @@ import React from 'react'; -import {render} from 'react-dom'; -import { Router, browserHistory } from 'react-router' +import { render } from 'react-dom'; +import { Router, browserHistory } from 'react-router'; +import { Provider } from 'react-redux'; +import { createStore, applyMiddleware, compose } from 'redux'; +import thunkMiddleware from 'redux-thunk'; import routeConfig from './routes'; -import { Provider } from 'react-redux' -import { createStore, applyMiddleware, compose } from 'redux' -import thunkMiddleware from 'redux-thunk' -import workflowApp from './reducers' +import workflowApp from './reducers'; +// eslint-disable-next-line no-underscore-dangle const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; -let store = createStore(workflowApp, composeEnhancers(applyMiddleware( - thunkMiddleware -))); +const store = createStore(workflowApp, composeEnhancers(applyMiddleware(thunkMiddleware))); function updateLocation() { store.dispatch({ type: 'LOCATION_UPDATED', - 'location' : this.state.location.key + location: this.state.location.key }); } render( - + , - document.getElementById('content')) + document.getElementById('content') +); diff --git a/ui/src/components/App.js b/ui/src/components/App.js index 2c7eef0b47..58ad345898 100644 --- a/ui/src/components/App.js +++ b/ui/src/components/App.js @@ -1,41 +1,53 @@ import React from 'react'; -import packageJSON from '../../package.json'; -import Footer from './common/Footer'; -import ErrorPage from './common/Error' -import LeftMenu from './common/LeftMenu' import { connect } from 'react-redux'; +import Footer from './common/Footer'; +import ErrorPage from './common/Error'; +import LeftMenu from './common/LeftMenu'; +import packageJSON from '../../package.json'; + +class App extends React.Component { + state = { + minimize: false + }; -const App = React.createClass({ - getInitialState() { - return { - minimize: false - }; - }, - handleResize(e) { - this.setState({windowWidth: window.innerWidth, minimize: window.innerWidth < 600}); - }, componentDidMount() { - window.addEventListener('resize', this.handleResize); - }, - componentWillUnmount() { - window.removeEventListener('resize', this.handleResize); - }, + window.addEventListener('resize', this.handleResize); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); + } + + handleResize = () => { + this.setState({ minimize: window.innerWidth < 600 }); + }; + render() { - const version = packageJSON.version; - const marginLeft = this.state.minimize?'52px':'177px'; + const { version } = packageJSON; + const marginLeft = this.state.minimize ? '52px' : '177px'; return !this.props.error ? ( -
-
- - -
- {this.props.children} +
+
+ + +
+ {this.props.children}
-
+
- ) : this.props.children; + ) : ( + this.props.children + ); } -}); +} export default connect(state => state.global)(App); diff --git a/ui/src/components/common/Error.js b/ui/src/components/common/Error.js index 6cfad925c2..994fb390ec 100644 --- a/ui/src/components/common/Error.js +++ b/ui/src/components/common/Error.js @@ -1,22 +1,21 @@ import React from 'react'; import { connect } from 'react-redux'; import { Panel, Button } from 'react-bootstrap'; -const ErrorPage = React.createClass({ - getInitialState() { - return { - alertVisible: false, - status: '', - details: '' - } - }, +class ErrorPage extends React.Component { + state = { + alertVisible: false, + status: '', + details: '' + }; + componentWillReceiveProps(nextProps) { let status = ''; let details = ''; - if(nextProps.exception != null && nextProps.exception.response != null){ - status = nextProps.exception.response.status + ' - ' + nextProps.exception.response.statusText; + if (nextProps.exception != null && nextProps.exception.response != null) { + status = `${nextProps.exception.response.status} - ${nextProps.exception.response.statusText}`; details = JSON.stringify(nextProps.exception.response.text); - }else { + } else { details = nextProps.exception; } this.setState({ @@ -24,10 +23,12 @@ const ErrorPage = React.createClass({ status, details }); - }, - handleAlertDismiss() { - this.setState({alertVisible: false}); - }, + } + + handleAlertDismiss = () => { + this.setState({ alertVisible: false }); + }; + render() { if (this.state.alertVisible) { return ( @@ -35,14 +36,15 @@ const ErrorPage = React.createClass({ {this.state.details} - +   If you think this is not expected, file a bug with workflow admins. ); - }else { - return (); } + return ; } -}); +} export default connect(state => state.workflow)(ErrorPage); diff --git a/ui/src/components/common/LeftMenu.js b/ui/src/components/common/LeftMenu.js index eaeb9aedaa..981fa30dab 100644 --- a/ui/src/components/common/LeftMenu.js +++ b/ui/src/components/common/LeftMenu.js @@ -1,143 +1,155 @@ import React from 'react'; -import { Link } from 'react-router' +import { Link } from 'react-router'; import { connect } from 'react-redux'; const menuPaths = { - Workflow: [{ - header: true, - label: 'Executions', - href: '/events', - icon: 'fa-star' - },{ - label: 'All', - href: '/workflow', - icon: 'fa-circle-thin' - },{ - label: 'Running', - href: '/workflow?status=RUNNING', - icon: 'fa-play-circle' - },{ - label: 'Failed', - href: '/workflow?status=FAILED&h=48', - icon: 'fa-warning' - },{ - label: 'Timed Out', - href: '/workflow?status=TIMED_OUT&h=48', - icon: 'fa-clock-o' - },{ - label: 'Terminated', - href: '/workflow?status=TERMINATED&h=48', - icon: 'fa-ban' - },{ - label: 'Completed', - href: '/workflow?status=COMPLETED&h=48', - icon: 'fa-bullseye' - },{ - header: true, - label: 'Metadata', - href: '/events', - icon: 'fa-star' - },{ - label: 'Workflow Defs', - href: '/workflow/metadata', - icon: 'fa-code-fork' - },{ - label: 'Tasks', - href: '/workflow/metadata/tasks', - icon: 'fa-tasks' - },{ - header: true, - label: 'Workflow Events', - href: '/events', - icon: 'fa-star' - },{ - label: 'Event Handlers', - href: '/events', - icon: 'fa-star' - },{ - header: true, - label: 'Task Queues', - href: '/events', - icon: 'fa-star' - },{ - label: 'Poll Data', - href: '/workflow/queue/data', - icon: 'fa-exchange' - }] + Workflow: [ + { + header: true, + label: 'Executions', + href: '/events', + icon: 'fa-star' + }, + { + label: 'All', + href: '/workflow', + icon: 'fa-circle-thin' + }, + { + label: 'Running', + href: '/workflow?status=RUNNING', + icon: 'fa-play-circle' + }, + { + label: 'Failed', + href: '/workflow?status=FAILED&h=48', + icon: 'fa-warning' + }, + { + label: 'Timed Out', + href: '/workflow?status=TIMED_OUT&h=48', + icon: 'fa-clock-o' + }, + { + label: 'Terminated', + href: '/workflow?status=TERMINATED&h=48', + icon: 'fa-ban' + }, + { + label: 'Completed', + href: '/workflow?status=COMPLETED&h=48', + icon: 'fa-bullseye' + }, + { + header: true, + label: 'Metadata', + href: '/events', + icon: 'fa-star' + }, + { + label: 'Workflow Defs', + href: '/workflow/metadata', + icon: 'fa-code-fork' + }, + { + label: 'Tasks', + href: '/workflow/metadata/tasks', + icon: 'fa-tasks' + }, + { + header: true, + label: 'Workflow Events', + href: '/events', + icon: 'fa-star' + }, + { + label: 'Event Handlers', + href: '/events', + icon: 'fa-star' + }, + { + header: true, + label: 'Task Queues', + href: '/events', + icon: 'fa-star' + }, + { + label: 'Poll Data', + href: '/workflow/queue/data', + icon: 'fa-exchange' + } + ] }; -const LeftMenu = React.createClass({ - - getInitialState() { - return { - sys: {}, - minimize: false - }; - }, - handleResize(e) { - this.setState({windowWidth: window.innerWidth, minimize: window.innerWidth < 600}); - }, +class LeftMenu extends React.Component { + state = { + minimize: false, + loading: false + }; componentDidMount() { - window.addEventListener('resize', this.handleResize); - }, + window.addEventListener('resize', this.handleResize); + } + + componentWillReceiveProps({ fetching: loading, minimize }) { + this.setState({ loading, minimize }); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); + } - componentWillUnmount() { - window.removeEventListener('resize', this.handleResize); - }, - componentWillReceiveProps(nextProps) { - this.state.loading = nextProps.fetching; - this.state.version = nextProps.version; - this.state.minimize = nextProps.minimize; - }, + handleResize = () => { + this.setState({ minimize: window.innerWidth < 600 }); + }; render() { - let minimize = this.state.minimize; - let appName = 'Workflow'; - const width = minimize?'50px':'176px'; + const { minimize } = this.state; + const { appName = 'Workflow' } = this.props; - if (this.props.appName) { - appName = this.props.appName; - } - let display = minimize?'none':''; - let menuItems = []; + const width = minimize ? '50px' : '176px'; + + const display = minimize ? 'none' : ''; + const menuItems = []; let keyVal = 0; - menuPaths[appName].map((cv, i, arr) => { - let iconClass = 'fa ' + cv['icon']; - if(cv['header'] == true) { - menuItems.push(( + + // eslint-disable-next-line array-callback-return + menuPaths[appName].map(cv => { + const iconClass = `fa ${cv.icon}`; + if (cv.header === true) { + menuItems.push(
-
 {cv['label']}
+
+  {cv.label} +
- )); + ); } else { - menuItems.push(( - -
- - - {cv['label']} - -
+ menuItems.push( + +
+ + {cv.label} +
- )); + ); } }); return ( -
+ ); } - -}); +} export default connect(state => state.workflow)(LeftMenu); diff --git a/ui/src/components/event/EventExecs.js b/ui/src/components/event/EventExecs.js index 5a00ebfe43..a63b353923 100644 --- a/ui/src/components/event/EventExecs.js +++ b/ui/src/components/event/EventExecs.js @@ -1,29 +1,26 @@ import React from 'react'; -import { Table, Grid, Row, Col } from 'react-bootstrap'; import { connect } from 'react-redux'; -import { getEventHandlers } from '../../actions/WorkflowActions'; import Typeahead from 'react-bootstrap-typeahead'; +import { Table, Grid, Row, Col } from 'react-bootstrap'; +import { getEventHandlers } from '../../actions/WorkflowActions'; + +class EventExecs extends React.Component { + state = { + events: [], + eventTypes: [] + }; -const EventExecs = React.createClass({ - getInitialState() { - return { - events: [], - executions: [], - eventTypes: [] - } - }, - componentWillMount(){ + componentWillMount() { this.props.dispatch(getEventHandlers()); - //this.props.dispatch(getEvents()); - }, - componentWillReceiveProps(nextProps) { - this.state.executions = nextProps.executions; - this.state.events = nextProps.events; - }, + } + + componentWillReceiveProps({ events }) { + this.setState({ events }); + } + render() { - let executions = this.state.executions; - let events = this.state.events; - let eventTypes = []; + const { events } = this.state; + const eventTypes = []; events.forEach(event => { eventTypes.push(event.event); }); @@ -32,27 +29,33 @@ const EventExecs = React.createClass({

Event Executions

Search for Event Executions

- + - + - + -
- - - - - - +
+
Something here
+ + + + +
Something here
); } -}); +} export default connect(state => state.workflow)(EventExecs); diff --git a/ui/src/components/event/EventList.js b/ui/src/components/event/EventList.js index dc4cbb72fc..739dab0481 100644 --- a/ui/src/components/event/EventList.js +++ b/ui/src/components/event/EventList.js @@ -1,119 +1,180 @@ +/* eslint-disable react/no-unescaped-entities */ import React from 'react'; import { Popover, OverlayTrigger, Table } from 'react-bootstrap'; import { connect } from 'react-redux'; import { getEventHandlers } from '../../actions/WorkflowActions'; -const Events = React.createClass({ - getInitialState() { - return { - events: [] - } - }, - componentWillMount(){ +class Events extends React.Component { + state = { + events: [] + }; + + componentWillMount() { this.props.dispatch(getEventHandlers()); - }, + } + componentWillReceiveProps(nextProps) { this.state.events = nextProps.events || []; - }, + } + render() { - var wfs = this.state.events; + const { events: wfs } = this.state; function helpName() { - return ( -
- Unique name identifying the event handler. -
- - }>
); + return ( + +
Unique name identifying the event handler.
+ + } + > + + + +
+ ); } function helpQueue() { - // - return ( -
-

Name of the Queue which the handler listens to. The supported queue systems are SQS and Conductor.

-

The name is prefixed by the source (sqs, conductor). e.g. sqs:sqs_queue_name

-

For SQS this is the name of the queue and NOT the URI of the queue.

-

For Conductor the name is same as 'sink' name provided for Event tasks.

-
- - }>
); + return ( + +
+

+ Name of the Queue which the handler listens to. The supported queue systems are SQS and{' '} + Conductor. +

+

The name is prefixed by the source (sqs, conductor). e.g. sqs:sqs_queue_name

+

For SQS this is the name of the queue and NOT the URI of the queue.

+

For Conductor the name is same as 'sink' name provided for Event tasks.

+
+ + } + > + + + +
+ ); } function helpCond() { - // - return ( -
-

An expression that can be evaluated with the payload in the queue.

-

The Actions are executed ONLY when the expression evaluation returns True

-

The expression follows Javascript for syntax

-

An empty / null expression is evaluated to True

-
- - }>
); + return ( + +
+

An expression that can be evaluated with the payload in the queue.

+

The Actions are executed ONLY when the expression evaluation returns True

+

The expression follows Javascript for syntax

+

An empty / null expression is evaluated to True

+
+ + } + > + + + +
+ ); } function helpActions() { - // - return ( -
-

Set of actions that are taken when a message arrives with payload that matches the condition.

-

Supported Actions are: start_workflow, complete_task and fail_task

-

For the detailed documentation on the syntax and parameters for each of these actions, visit the doumentation link

-
- - }>
); + return ( + +
+

Set of actions that are taken when a message arrives with payload that matches the condition.

+

Supported Actions are: start_workflow, complete_task and fail_task

+

+ For the detailed documentation on the syntax and parameters for each of these actions, visit the + doumentation link +

+
+ + } + > + + + +
+ ); + } + function nameMaker(cell, row) { + return ( + +
+
{JSON.stringify(row, null, 2)}
+
+ + } + > + {cell} +
+ ); } - function nameMaker(cell, row){ - return (
-
{JSON.stringify(row, null, 2)}
-
- }>{cell}
); - }; function getActions(eh) { - let trs = []; - let actions = eh.actions || []; + const trs = []; + const actions = eh.actions || []; actions.forEach(action => { - let row =
{action.action}
{JSON.stringify(action[action.action], null, 2)}
+ const row =
{action.action}
{JSON.stringify(action[action.action], null, 2)}
; trs.push(row); }); return
{trs}
; } function tableBody(events) { - let trs = []; + const trs = []; events.forEach(eh => { - let row = - {nameMaker(eh.name, eh)} - {eh.event} - {eh.condition} - {getActions(eh)} - {eh.active?'Yes':'No'} - ; - let actionRows = {getActions(eh)} + const row = ( + + {nameMaker(eh.name, eh)} + {eh.event} + {eh.condition} + {getActions(eh)} + {eh.active ? 'Yes' : 'No'} + + ); + trs.push(row); }); - return {trs} + return {trs}; } return (

Event Handlers

- - - - - - - - - - +
Name {helpName()}Event / Queue {helpQueue()}Condition {helpCond()}Actions {helpActions()}Active?
+ + + + + + + + + {tableBody(wfs)}
Name {helpName()}Event / Queue {helpQueue()}Condition {helpCond()}Actions {helpActions()}Active?
); } -}); +} + export default connect(state => state.workflow)(Events); diff --git a/ui/src/components/workflow/WorkflowMetaList.js b/ui/src/components/workflow/WorkflowMetaList.js index 2bb6c4ebd3..609d8eede2 100644 --- a/ui/src/components/workflow/WorkflowMetaList.js +++ b/ui/src/components/workflow/WorkflowMetaList.js @@ -1,53 +1,65 @@ import React from 'react'; import { Link } from 'react-router'; -import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'; import { connect } from 'react-redux'; +import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; import { getWorkflowDefs } from '../../actions/WorkflowActions'; -const WorkflowMetaList = React.createClass({ - getInitialState() { - return { - name: '', - version: '', - workflows: [] - } - }, - componentWillMount(){ +class WorkflowMetaList extends React.Component { + state = { + workflows: [] + }; + + componentWillMount() { this.props.dispatch(getWorkflowDefs()); - }, - componentWillReceiveProps(nextProps){ - this.state.workflows = nextProps.workflows; + } + + componentWillReceiveProps({ workflows }) { + this.setState({ workflows }); + } - }, render() { - var wfs = this.state.workflows; + const { workflows } = this.state; - function jsonMaker(cell, row){ + function jsonMaker(cell) { return JSON.stringify(cell); - }; + } - function taskMaker(cell, row){ - if(cell == null){ + function taskMaker(cell) { + if (cell == null) { return ''; } - return JSON.stringify(cell.map(task => {return task.name;})); - }; + return JSON.stringify( + cell.map(task => { + return task.name; + }) + ); + } - function nameMaker(cell, row){ - return ({row.name} / {row.version}); - }; + function nameMaker(cell, row) { + return ( + + {row.name} / {row.version} + + ); + } return (

Workflows

- - Name/Version - Input Parameters - - + + + Name/Version + + + Input Parameters + + +
); } -}); +} export default connect(state => state.workflow)(WorkflowMetaList); diff --git a/ui/src/components/workflow/executions/WorkflowAction.js b/ui/src/components/workflow/executions/WorkflowAction.js index 8ebb3e6351..0968d1cc5a 100644 --- a/ui/src/components/workflow/executions/WorkflowAction.js +++ b/ui/src/components/workflow/executions/WorkflowAction.js @@ -1,142 +1,133 @@ import React from 'react'; import { Button, ButtonGroup, OverlayTrigger, Popover } from 'react-bootstrap'; import { connect } from 'react-redux'; -import { terminateWorkflow, restartWorfklow, retryWorfklow, pauseWorfklow, resumeWorfklow } from '../../../actions/WorkflowActions'; +import { + terminateWorkflow, + restartWorfklow, + retryWorfklow, + pauseWorfklow, + resumeWorfklow +} from '../../../actions/WorkflowActions'; + +class WorkflowAction extends React.Component { + terminate = () => { + this.props.dispatch(terminateWorkflow(this.props.workflowId)); + }; + + restart = () => { + this.props.dispatch(restartWorfklow(this.props.workflowId)); + }; + + retry = () => { + this.props.dispatch(retryWorfklow(this.props.workflowId)); + }; + + pause = () => { + this.props.dispatch(pauseWorfklow(this.props.workflowId)); + }; + + resume = () => { + this.props.dispatch(resumeWorfklow(this.props.workflowId)); + }; -const WorkflowAction = React.createClass({ - getInitialState() { - return { - terminating: false, - rerunning: false, - restarting: false, - retrying: false, - pausing: false, - resuming: false - }; - }, render() { - const tt_term = ( + const ttTerm = ( - Terminate workflow execution. All running tasks will be cancelled. + Terminate workflow execution. All running tasks will be cancelled. ); - const tt_restart = ( + + const ttRestart = ( Restart the workflow from the begining (First Task) ); - const tt_retry = ( + + const ttRetry = ( Retry the last failed task and put workflow in running state ); - const tt_pause = ( + + const ttPause = ( - Pauses workflow execution. No new tasks will be scheduled until workflow has been resumed. + Pauses workflow execution. No new tasks will be scheduled until workflow has been resumed. ); - const tt_resume = ( + + const ttResume = ( Resume workflow execution ); - - const { - terminating, - restarting, - retrying, - pausing, - resuming - } = this.props; - if(this.props.workflowStatus == 'RUNNING'){ + const { terminating, restarting, retrying, pausing, resuming } = this.props; + if (this.props.workflowStatus === 'RUNNING') { return ( - + - - - ); - - }if(this.props.workflowStatus == 'COMPLETED'){ + } + if (this.props.workflowStatus === 'COMPLETED') { return ( - - + + ); - }else if(this.props.workflowStatus == 'FAILED' || this.props.workflowStatus == 'TERMINATED'){ + } else if (this.props.workflowStatus === 'FAILED' || this.props.workflowStatus === 'TERMINATED') { return ( - - - - - - ); - }else if(this.props.workflowStatus == 'PAUSED'){ - return ( - - - - - + ); - }else { - + } else if (this.props.workflowStatus === 'PAUSED') { return ( - - ); - } - }, - terminate(){ - this.setState({terminating: true}); - this.props.dispatch(terminateWorkflow(this.props.workflowId)); - }, - rerun(){ - this.setState({rerunning: true}); - }, - restart(){ - this.setState({restarting: true}); - this.props.dispatch(restartWorfklow(this.props.workflowId)); - }, - retry(){ - this.setState({retrying: true}); - this.props.dispatch(retryWorfklow(this.props.workflowId)); - }, - pause(){ - this.setState({pausing: true}); - this.props.dispatch(pauseWorfklow(this.props.workflowId)); - }, - resume(){ - this.setState({resuming: true}); - this.props.dispatch(resumeWorfklow(this.props.workflowId)); + return ( + + + + + + ); } -}); +} export default connect(state => state.workflow)(WorkflowAction); diff --git a/ui/src/components/workflow/executions/WorkflowList.js b/ui/src/components/workflow/executions/WorkflowList.js index 7c1326b137..6ad7229be9 100644 --- a/ui/src/components/workflow/executions/WorkflowList.js +++ b/ui/src/components/workflow/executions/WorkflowList.js @@ -1,296 +1,400 @@ +/* eslint-disable no-restricted-globals */ + import React from 'react'; import { Link } from 'react-router'; -import { Input, Button, Panel, Popover, OverlayTrigger, ButtonGroup, Grid, Row, Col } from 'react-bootstrap'; -import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'; import { connect } from 'react-redux'; -import { searchWorkflows, getWorkflowDefs } from '../../../actions/WorkflowActions'; import Typeahead from 'react-bootstrap-typeahead'; +import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; +import { Input, Button, Panel, Popover, OverlayTrigger, ButtonGroup, Grid, Row, Col } from 'react-bootstrap'; +import { searchWorkflows, getWorkflowDefs } from '../../../actions/WorkflowActions'; function linkMaker(cell) { - return {cell}; + return {cell}; } function zeroPad(num) { - return ('0' + num).slice(-2); + return `0${num}`.slice(-2); } -function formatDate(cell){ - if(cell == null || !cell.split) { - return ''; - } - let c = cell.split("T"); - let time = c[1].split(":"); - let hh = zeroPad(time[0]); - let mm = zeroPad(time[1]); - let ss = zeroPad(time[2].replace("Z","")); +function formatDate(cell) { + if (cell == null || !cell.split) { + return ''; + } + const c = cell.split('T'); + const time = c[1].split(':'); + const hh = zeroPad(time[0]); + const mm = zeroPad(time[1]); + const ss = zeroPad(time[2].replace('Z', '')); - let dt = c[0] + "T" + hh + ":" + mm + ":" + ss + "Z"; + const dt = `${c[0]}T${hh}:${mm}:${ss}Z`; - if(dt == null || dt === ''){ - return ''; - } + if (dt == null || dt === '') { + return ''; + } - return new Date(dt).toLocaleString('en-US'); + return new Date(dt).toLocaleString('en-US'); } -function miniDetails(cell, row){ - return ( - {row.reasonForIncompletion == null?'':{row.reasonForIncompletion}
}
- Input
- {row.input} -
Output
+function miniDetails(cell, row) { + return ( + + + + {row.reasonForIncompletion == null ? ( + '' + ) : ( + + {row.reasonForIncompletion} +
+
+ )} +
+ Input +
+ + {row.input} + +
+ Output +
{row.output} -

- - - }>
); +
+
+ + } + > + +
+
+ ); } -const Workflow = React.createClass({ - getInitialState() { - let workflowTypes = this.props.location.query.workflowTypes; - if(workflowTypes != null && workflowTypes != '') { - workflowTypes = workflowTypes.split(','); - }else { - workflowTypes = []; - } - let status = this.props.location.query.status; - if(status != null && status != '') { - status = status.split(','); - }else { - status = []; - } - let search = this.props.location.query.q; - if(search == null || search == 'undefined' || search == '') { - search = ''; - } - let st = this.props.location.query.start; - let start = 0; - if(!isNaN(st)) { - start = parseInt(st); - } +class Workflow extends React.Component { + constructor(props) { + super(props); + const { + location: { + query: { workflowTypes = '', q = '', status = '', start = 0 } + } + } = props; - return { - search: search, - workflowTypes: workflowTypes, - status: status, + this.state = { + search: q === 'undefined' || q === '' ? '' : q, + workflowTypes: workflowTypes === '' ? [] : workflowTypes.split(','), + status: status !== '' ? status.split(',') : [], h: this.props.location.query.h, workflows: [], update: true, fullstr: true, - start: start - } - }, - componentWillMount(){ + start: !isNaN(start, 10) ? parseInt(start, 10) : start + }; + } + + componentWillMount() { this.props.dispatch(getWorkflowDefs()); this.doDispatch(); - }, - componentWillReceiveProps(nextProps) { - let workflowDefs = nextProps.workflows; - workflowDefs = workflowDefs ? workflowDefs : []; - workflowDefs = workflowDefs.map(workflowDef => workflowDef.name); - - let search = nextProps.location.query.q; - if(search == null || search == 'undefined' || search == '') { - search = ''; - } - let h = nextProps.location.query.h; - if(isNaN(h)) { - h = ''; - } - let start = nextProps.location.query.start; - if(isNaN(start)) { - start = 0; + } + + componentWillReceiveProps({ + workflows = [], + location: { + query: { h, start, status = '', q } } - let status = nextProps.location.query.status; - if(status != null && status != '') { - status = status.split(','); - }else { - status = []; + }) { + const workflowDefs = workflows.map(workflowDef => workflowDef.name); + + let search = q; + if (search == null || search === 'undefined' || search === '') { + search = ''; } let update = true; - update = this.state.search != search; - update = update || (this.state.h != h); - update = update || (this.state.start != start); - update = update || (this.state.status.join(',') != status.join(',')); + update = this.state.search !== search; + update = update || this.state.h !== h; + update = update || this.state.start !== start; this.setState({ - search : search, - h : h, - update : update, - status : status, - workflows : workflowDefs, - start : start + search, + h: isNaN(h, 10) ? '' : h, + update, + status: status !== '' ? status.split(',') : [], + workflows: workflowDefs, + start: isNaN(start, 10) ? 0 : start }); this.refreshResults(); - }, - searchBtnClick() { + } + + searchBtnClick = () => { this.state.update = true; this.refreshResults(); - }, - refreshResults() { - if(this.state.update) { + }; + + refreshResults = () => { + if (this.state.update) { this.state.update = false; this.urlUpdate(); this.doDispatch(); } - }, - urlUpdate() { - let q = this.state.search; - let h = this.state.h; - let workflowTypes = this.state.workflowTypes; - let status = this.state.status; - let start = this.state.start; - this.props.history.pushState(null, "/workflow?q=" + q + "&h=" + h + "&workflowTypes=" + workflowTypes + "&status=" + status + "&start=" + start); - }, - doDispatch() { - let search = ''; - if(this.state.search != '') { - search = this.state.search; - } - let h = this.state.h; - let query = []; + }; + + urlUpdate = () => { + const { workflowTypes, status, start, h, search: q } = this.state; + + this.props.history.pushState( + null, + `/workflow?q=${q}&h=${h}&workflowTypes=${workflowTypes}&status=${status}&start=${start}` + ); + }; - if(this.state.workflowTypes.length > 0) { - query.push('workflowType IN (' + this.state.workflowTypes.join(',') + ') '); + doDispatch = () => { + const { search = '' } = this.state; + const query = []; + + if (this.state.workflowTypes.length > 0) { + query.push(`workflowType IN (${this.state.workflowTypes.join(',')}) `); } - if(this.state.status.length > 0) { - query.push('status IN (' + this.state.status.join(',') + ') '); + if (this.state.status.length > 0) { + query.push(`status IN (${this.state.status.join(',')}) `); } - this.props.dispatch(searchWorkflows(query.join(' AND '), search, this.state.h, this.state.fullstr, this.state.start)); - }, - workflowTypeChange(workflowTypes) { + this.props.dispatch( + searchWorkflows(query.join(' AND '), search, this.state.h, this.state.fullstr, this.state.start) + ); + }; + + workflowTypeChange = workflowTypes => { this.state.update = true; this.state.workflowTypes = workflowTypes; this.refreshResults(); - }, - statusChange(status) { + }; + + statusChange = status => { this.state.update = true; this.state.status = status; this.refreshResults(); - }, - nextPage() { - this.state.start = 100 + parseInt(this.state.start); + }; + + nextPage = () => { + this.state.start = 100 + parseInt(this.state.start, 10); this.state.update = true; this.refreshResults(); - }, - prevPage() { - this.state.start = parseInt(this.state.start) - 100; - if(this.state.start < 0) { - this.state.start = 0; + }; + + prevPage = () => { + this.state.start = parseInt(this.state.start, 10) - 100; + if (this.state.start < 0) { + this.state.start = 0; } this.state.update = true; this.refreshResults(); - }, - searchChange(e){ - let val = e.target.value; + }; + + searchChange = e => { + const val = e.target.value; this.setState({ search: val }); - }, - hourChange(e){ + }; + + hourChange = e => { this.state.update = true; this.state.h = e.target.value; this.refreshResults(); - }, - keyPress(e){ - if(e.key == 'Enter'){ - this.state.update = true; - var q = e.target.value; - this.setState({search: q}); - this.refreshResults(); - } - }, - prefChange(e) { + }; + + keyPress = e => { + if (e.key == 'Enter') { + this.state.update = true; + var q = e.target.value; + this.setState({ search: q }); + this.refreshResults(); + } + }; + + prefChange = e => { this.setState({ - fullstr:e.target.checked + fullstr: e.target.checked }); this.state.update = true; this.refreshResults(); - }, - render() { + }; + + render() { let wfs = []; let filteredWfs = []; - let totalHits = 0; + let totalHits = 0; let found = 0; - if(this.props.data.hits) { + if (this.props.data.hits) { wfs = this.props.data.hits; totalHits = this.props.data.totalHits; found = wfs.length; } - let start = parseInt(this.state.start); + const start = parseInt(this.state.start); let max = start + 100; - if(found < 100) { + if (found < 100) { max = start + found; } - const workflowNames = this.state.workflows?this.state.workflows:[]; - const statusList = ['RUNNING','COMPLETED','FAILED','TIMED_OUT','TERMINATED','PAUSED']; + const workflowNames = this.state.workflows ? this.state.workflows : []; + const statusList = ['RUNNING', 'COMPLETED', 'FAILED', 'TIMED_OUT', 'TERMINATED', 'PAUSED']; - //secondary filter to match sure we only show workflows that match the the status + // secondary filter to match sure we only show workflows that match the the status var currentStatusArray = this.state.status; - if(currentStatusArray.length>0 && wfs.length>0) { - filteredWfs = wfs.filter( function (wf) { - return currentStatusArray.includes(wf.status); //remove wft if status doesn't match search - }); + if (currentStatusArray.length > 0 && wfs.length > 0) { + filteredWfs = wfs.filter(wf => currentStatusArray.includes(wf.status)); } else { - filteredWfs = wfs; + filteredWfs = wfs; } return (
- - - - -     -    + + + + +     +    + - - -     - - - -     - - - -     -
      - -
-
-
- -
+ + +     + + + +     + + + +     +
      + +
+
+
- Total Workflows Found: {totalHits}, Displaying {this.state.start} to {max} - - {parseInt(this.state.start) >= 100? Previous Page:''} - {parseInt(this.state.start) + 100 <= totalHits?  Next Page :''} + + Total Workflows Found: {totalHits}, Displaying {this.state.start} to {max} - - Workflow - Workflow ID - Status - Start Time - Last Updated - - - - Input -   + + {parseInt(this.state.start, 10) >= 100 ? ( + +  Previous Page + + ) : ( + '' + )} + {parseInt(this.state.start, 10) + 100 <= totalHits ? ( + +   Next Page  + + ) : ( + '' + )} + + + + Workflow + + + Workflow ID + + + Status + + + Start Time + + + Last Updated + + + + + + Input + + +   + -

+
+
); } -}); +} export default connect(state => state.workflow)(Workflow); diff --git a/ui/src/components/workflow/queues/QueueList.js b/ui/src/components/workflow/queues/QueueList.js index 288a0b77bf..a0af22a3bc 100644 --- a/ui/src/components/workflow/queues/QueueList.js +++ b/ui/src/components/workflow/queues/QueueList.js @@ -1,50 +1,53 @@ import React from 'react'; import moment from 'moment'; -import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'; +import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; import { connect } from 'react-redux'; import { getQueueData } from '../../../actions/WorkflowActions'; -const QueueListList = React.createClass({ - getInitialState() { - return { - name: '', - version: '', - queueData: [] - } - }, - componentWillMount(){ +class QueueListList extends React.Component { + state = { + queueData: [] + }; + + componentWillMount() { this.props.dispatch(getQueueData()); - }, - componentWillReceiveProps(nextProps){ - this.state.queueData = nextProps.queueData; - }, + } + + componentWillReceiveProps({ queueData }) { + this.setState({ queueData }); + } + render() { - var wfs = this.state.queueData; + const { queueData } = this.state; - function formatName(cell, row){ - var name = row.queueName; - if(row.domain != null){ - name = name + " (" + row.domain + ")"; - } - return name; - }; + function formatName(_, { queueName, domain }) { + return domain != null ? `${queueName} (${domain})` : queueName; + } - function formatDate(cell, row){ + function formatDate(_, row) { return moment(row.lastPollTime).fromNow(); - }; + } return (

Queues

- - Name (Domain) - Size - Last Poll Time - Last Polled By - + + + Name (Domain) + + + Size + + + Last Poll Time + + + Last Polled By + +
); } -}); +} export default connect(state => state.workflow)(QueueListList); diff --git a/ui/src/components/workflow/tasks/TasksMetaList.js b/ui/src/components/workflow/tasks/TasksMetaList.js index 725c263662..cda83ba9ad 100644 --- a/ui/src/components/workflow/tasks/TasksMetaList.js +++ b/ui/src/components/workflow/tasks/TasksMetaList.js @@ -1,72 +1,132 @@ import React from 'react'; import { Input, Popover, OverlayTrigger } from 'react-bootstrap'; -import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'; +import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; import { connect } from 'react-redux'; import { getTaskDefs } from '../../../actions/WorkflowActions'; -const TaskMetaList = React.createClass({ - getInitialState() { - return { - name: '', - version: '', - taskDefs: [] - } - }, - componentWillMount(){ +class TaskMetaList extends React.Component { + state = { + taskDefs: [] + }; + + componentWillMount() { this.props.dispatch(getTaskDefs()); - }, - componentWillReceiveProps(nextProps){ - this.state.taskDefs = nextProps.taskDefs; + } + + componentWillReceiveProps({ taskDefs }) { + this.setState({ taskDefs }); + } - }, render() { - var wfs = this.state.taskDefs; + const { taskDefs } = this.state; - function retries(cell, row){ - if(row.retryLogic == 'FIXED') { - return row.retryLogic + ' (' + row.retryDelaySeconds + ' seconds)'; - } + const retries = (_, row) => { + return row.retryLogic === 'FIXED' ? `${row.retryLogic} (${row.retryDelaySeconds} seconds)` : ''; }; - function editor(cell, row){ - return (
- -
- - - -
-
- - - -
-
-
-
-
- -
- }>{cell}
); - }; + function editor(cell, row) { + return ( + +
+
+ +
+ + + + +
+ +
+ + + + +
+ +
+ +
+ +
+ +
+
+
+ + } + > + {cell} +
+ ); + } return (

Task Definitions

- - Name/Version - Owner App - Timeout Policy - Timeout Seconds - Response Timeout Seconds - Retry Count - Concurrent Exec Limit - Retry Logic - + + + Name/Version + + + Owner App + + + Timeout Policy + + + Timeout Seconds + + + Response Timeout Seconds + + + Retry Count + + + Concurrent Exec Limit + + + Retry Logic + +
); } -}); +} export default connect(state => state.workflow)(TaskMetaList); diff --git a/ui/src/server.js b/ui/src/server.js index a3ba4c3883..1c310f3f0e 100644 --- a/ui/src/server.js +++ b/ui/src/server.js @@ -3,7 +3,7 @@ import express from 'express'; import Bunyan from 'bunyan'; import MiddlewareIndex from './api/middleware'; -let log = Bunyan.createLogger({ src: true, name: 'Conductor UI' }); +const log = Bunyan.createLogger({ src: true, name: 'Conductor UI' }); const wfeAPI = require('./api/wfe'); const sysAPI = require('./api/sys'); @@ -20,33 +20,32 @@ class Main { this.startServer(app); } - preMiddlewareConfig(app, middlewareIndex) { + preMiddlewareConfig = (app, middlewareIndex) => { middlewareIndex.before(app); - } + }; - routesConfig(app) { - log.info('Serving static ' + process.cwd()); + routesConfig = app => { + log.info(`Serving static ${process.cwd()}`); app.use(express.static('public')); app.use('/api/wfe', wfeAPI); app.use('/api/sys', sysAPI); app.use('/api/events', eventsAPI); - } + }; - postMiddlewareConfig(app, middlewareIndex) { + postMiddlewareConfig = (app, middlewareIndex) => { middlewareIndex.after(app); - } + }; - startServer(app) { - let server = app.listen(process.env.NODE_PORT || 5000, function() { - var host = server.address().address; - var port = server.address().port; - log.info('Workflow UI listening at http://%s:%s', host, port); + startServer = app => { + const server = app.listen(process.env.NODE_PORT || 5000, () => { + const { address, port } = server.address(); + log.info('Workflow UI listening at http://%s:%s', address, port); if (process.send) { process.send('online'); } }); - } + }; } const main = new Main(); diff --git a/ui/test/filters.test.js b/ui/test/filters.test.js index cbd9783839..1a5eb03da6 100644 --- a/ui/test/filters.test.js +++ b/ui/test/filters.test.js @@ -1,11 +1,12 @@ +/* eslint-disable no-undef */ import assert from 'assert'; import AuthFilter from '../src/api/middleware/filters/authFilter'; -describe('Filters', function() { - describe('Pre Middleware', function() { +describe('Filters', () => { + describe('Pre Middleware', () => { const authFilter = new AuthFilter(); - it('should add token to req and call next', function() { + it('should add token to req and call next', () => { const middleware = []; // Create Mock App @@ -33,7 +34,7 @@ describe('Filters', function() { }); }); - it('should bypass add auth token if auth header not present and call next', function() { + it('should bypass add auth token if auth header not present and call next', () => { const middleware = []; // Create Mock App