Repository: aurora Updated Branches: refs/heads/master 39337c3ee -> f6c7d6c23
Add task page to Scheduler UI (without inbound links yet - this is for external referencing). Reviewed at https://reviews.apache.org/r/65494/ Project: http://git-wip-us.apache.org/repos/asf/aurora/repo Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/f6c7d6c2 Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/f6c7d6c2 Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/f6c7d6c2 Branch: refs/heads/master Commit: f6c7d6c2385367df936be64c3f740b0941535b67 Parents: 39337c3 Author: David McLaughlin <da...@dmclaughlin.com> Authored: Mon Feb 5 17:26:11 2018 -0800 Committer: David McLaughlin <da...@dmclaughlin.com> Committed: Mon Feb 5 17:26:11 2018 -0800 ---------------------------------------------------------------------- ui/src/main/js/components/Breadcrumb.js | 8 ++- ui/src/main/js/components/TaskStatus.js | 4 +- .../js/components/__tests__/Breadcrumb-test.js | 8 +++ ui/src/main/js/index.js | 5 ++ ui/src/main/js/pages/Task.js | 63 ++++++++++++++++++++ ui/src/main/js/pages/__tests__/Task-test.js | 60 +++++++++++++++++++ ui/src/main/sass/components/_instance-page.scss | 2 +- 7 files changed, 146 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/js/components/Breadcrumb.js ---------------------------------------------------------------------- diff --git a/ui/src/main/js/components/Breadcrumb.js b/ui/src/main/js/components/Breadcrumb.js index 76c6270..db5d7b2 100644 --- a/ui/src/main/js/components/Breadcrumb.js +++ b/ui/src/main/js/components/Breadcrumb.js @@ -5,7 +5,7 @@ function url(...args) { return args.join('/'); } -export default function Breadcrumb({ cluster, role, env, name, instance, update }) { +export default function Breadcrumb({ cluster, role, env, name, instance, taskId, update }) { const crumbs = [<Link key='cluster' to='/scheduler'>{cluster}</Link>]; if (role) { crumbs.push(<span key='role-divider'>/</span>); @@ -31,6 +31,12 @@ export default function Breadcrumb({ cluster, role, env, name, instance, update {update} </Link>); } + if (taskId) { + crumbs.push(<span key='update-divider'>/</span>); + crumbs.push(<Link key='task' to={`/scheduler/${url(role, env, name, 'task', taskId)}`}> + {taskId} + </Link>); + } return (<div className='aurora-breadcrumb'> <div className='container'> <h2>{crumbs}</h2> http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/js/components/TaskStatus.js ---------------------------------------------------------------------- diff --git a/ui/src/main/js/components/TaskStatus.js b/ui/src/main/js/components/TaskStatus.js index 2e5a2b4..96d19b2 100644 --- a/ui/src/main/js/components/TaskStatus.js +++ b/ui/src/main/js/components/TaskStatus.js @@ -8,7 +8,7 @@ import TaskNeighbors from 'components/TaskNeighbors'; import { isNully } from 'utils/Common'; import { getClassForScheduleStatus, taskToStateMachine } from 'utils/Task'; -export default function TaskStatus({ task, neighbors }) { +export default function TaskStatus({ task, title, neighbors }) { if (isNully(task)) { return (<Container> <PanelGroup title={<StandardPanelTitle title='Active Task' />}> @@ -18,7 +18,7 @@ export default function TaskStatus({ task, neighbors }) { } return (<Container> - <PanelGroup title={<StandardPanelTitle title='Active Task' />}> + <PanelGroup title={<StandardPanelTitle title={title || 'Active Task'} />}> <div className='row'> <div className='col-md-6'> <TaskDetails task={task} /> http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/js/components/__tests__/Breadcrumb-test.js ---------------------------------------------------------------------- diff --git a/ui/src/main/js/components/__tests__/Breadcrumb-test.js b/ui/src/main/js/components/__tests__/Breadcrumb-test.js index 47f7afb..77faf28 100644 --- a/ui/src/main/js/components/__tests__/Breadcrumb-test.js +++ b/ui/src/main/js/components/__tests__/Breadcrumb-test.js @@ -29,4 +29,12 @@ describe('Breadcrumb', () => { expect(el.contains(<Link to='/scheduler/www-data/prod/hello'>hello</Link>)).toBe(true); expect(el.find(Link).length).toBe(4); }); + + it('Should render taskId crumb', () => { + const el = shallow( + <Breadcrumb cluster='devcluster' env='prod' name='hello' role='www-data' taskId='task-id' />); + expect(el.contains( + <Link to='/scheduler/www-data/prod/hello/task/task-id'>task-id</Link>)).toBe(true); + expect(el.find(Link).length).toBe(5); + }); }); http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/js/index.js ---------------------------------------------------------------------- diff --git a/ui/src/main/js/index.js b/ui/src/main/js/index.js index 9f94d4b..2c8fa27 100644 --- a/ui/src/main/js/index.js +++ b/ui/src/main/js/index.js @@ -8,6 +8,7 @@ import Home from 'pages/Home'; import Instance from 'pages/Instance'; import Job from 'pages/Job'; import Jobs from 'pages/Jobs'; +import Task from 'pages/Task'; import Update from 'pages/Update'; import Updates from 'pages/Updates'; @@ -30,6 +31,10 @@ const SchedulerUI = () => ( exact path='/scheduler/:role/:environment/:name/:instance' /> <Route + component={injectApi(Task)} + exact + path='/scheduler/:role/:environment/:name/task/:taskId' /> + <Route component={injectApi(Update)} exact path='/scheduler/:role/:environment/:name/update/:uid' /> http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/js/pages/Task.js ---------------------------------------------------------------------- diff --git a/ui/src/main/js/pages/Task.js b/ui/src/main/js/pages/Task.js new file mode 100644 index 0000000..457586a --- /dev/null +++ b/ui/src/main/js/pages/Task.js @@ -0,0 +1,63 @@ +import React from 'react'; + +import Breadcrumb from 'components/Breadcrumb'; +import Loading from 'components/Loading'; +import TaskStatus from 'components/TaskStatus'; + +import { isNullyOrEmpty } from 'utils/Common'; + +export default class Task extends React.Component { + constructor(props) { + super(props); + this.state = { + cluster: props.cluster || '', + loading: isNullyOrEmpty(props.task), + task: props.task + }; + } + + componentWillMount() { + const {api, match: {params: {role, environment, name, taskId}}} = this.props; + const that = this; + + const taskQuery = new TaskQuery(); + taskQuery.role = role; + taskQuery.environment = environment; + taskQuery.jobName = name; + taskQuery.taskIds = [taskId]; + api.getTasksWithoutConfigs(taskQuery, (response) => { + const tasks = response.result.scheduleStatusResult.tasks; + if (!isNullyOrEmpty(tasks)) { + that.setState({ + cluster: response.serverInfo.clusterName, + loading: false, + task: response.result.scheduleStatusResult.tasks[0] + }); + } else { + that.setState({ + cluster: response.serverInfo.clusterName, + loading: false + }); + } + }); + } + + render() { + if (this.state.loading) { + return <Loading />; + } else if (isNullyOrEmpty(this.state.task)) { + return <div>Task not found, it may have been automatically pruned.</div>; + } + + return (<div className='task-page'> + <Breadcrumb + cluster={this.state.cluster} + env={this.props.match.params.environment} + name={this.props.match.params.name} + role={this.props.match.params.role} + taskId={this.props.match.params.taskId} /> + + <TaskStatus task={this.state.task} title={`Task: ${this.props.match.params.taskId}`} /> + </div>); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/js/pages/__tests__/Task-test.js ---------------------------------------------------------------------- diff --git a/ui/src/main/js/pages/__tests__/Task-test.js b/ui/src/main/js/pages/__tests__/Task-test.js new file mode 100644 index 0000000..93ea0cc --- /dev/null +++ b/ui/src/main/js/pages/__tests__/Task-test.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import Task from '../Task'; + +import Breadcrumb from 'components/Breadcrumb'; +import Loading from 'components/Loading'; +import TaskStatus from 'components/TaskStatus'; + +const TEST_CLUSTER = 'test-cluster'; + +const params = { + role: 'test-role', + environment: 'test-env', + name: 'test-job', + taskId: 'task-id1' +}; + +function createMockApi(tasks) { + const api = {}; + api.getTasksWithoutConfigs = (query, handler) => handler({ + result: { + scheduleStatusResult: { + tasks: tasks + } + }, + serverInfo: { + clusterName: TEST_CLUSTER + } + }); + return api; +} + +const tasks = [{ + status: ScheduleStatus.RUNNING +}]; + +function apiSpy() { + return { + getTasksWithoutConfigs: jest.fn() + }; +} + +describe('Task', () => { + it('Should render Loading before data is fetched', () => { + expect( + shallow(<Task api={apiSpy()} match={{params: params}} />).contains(<Loading />)).toBe(true); + }); + + it('Should render page elements when tasks are fetched', () => { + const el = shallow(<Task api={createMockApi(tasks)} match={{params: params}} />); + expect(el.contains(<Breadcrumb + cluster={TEST_CLUSTER} + env={params.environment} + name={params.name} + role={params.role} + taskId={params.taskId} />)).toBe(true); + expect(el.contains(<TaskStatus task={tasks[0]} title={`Task: ${params.taskId}`} />)).toBe(true); + }); +}); http://git-wip-us.apache.org/repos/asf/aurora/blob/f6c7d6c2/ui/src/main/sass/components/_instance-page.scss ---------------------------------------------------------------------- diff --git a/ui/src/main/sass/components/_instance-page.scss b/ui/src/main/sass/components/_instance-page.scss index 1da87dc..1a8d130 100644 --- a/ui/src/main/sass/components/_instance-page.scss +++ b/ui/src/main/sass/components/_instance-page.scss @@ -1,4 +1,4 @@ -.instance-page { +.instance-page, .task-page { .active-task-details { h5, .task-details-title { text-transform: uppercase;