Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SingularityUI/app/components/common/Navigation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const Navigation = (props) => {
<MenuItem eventKey={5.2} onClick={(e) => goTo(props.router, e, 'slaves')}>Slaves</MenuItem>
<MenuItem eventKey={5.3} onClick={(e) => goTo(props.router, e, 'webhooks')}>Webhooks</MenuItem>
<MenuItem divider={true} />
<MenuItem eventKey={5.4} onClick={(e) => goTo(props.router, e, 'taskSearch')}>Task search</MenuItem>
<MenuItem eventKey={5.4} onClick={(e) => goTo(props.router, e, 'task-search')}>Task search</MenuItem>
</NavDropdown>
<NavItem eventKey={6} target="blank" href={config.apiDocs}>API Docs <small>(Beta)</small></NavItem>
<NavItem eventKey={7} className="global-search-button" onClick={(e) => handleSearchClick(e, props.toggleGlobalSearch)}>
Expand Down
11 changes: 5 additions & 6 deletions SingularityUI/app/components/common/ServerSideTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ export default class ServerSideTable extends SimpleTable {
}

updateDisplay(nextProps) {
const newState = {};
if (this.props.entries && this.props.entries.length > 0 && nextProps.entries.length === 0 && this.state.serverPage > 1) {
let newState = {};
if (this.didFetchParamsUpdate(nextProps)) {
this.props.dispatch(nextProps.fetchAction.trigger(...nextProps.fetchParams, nextProps.perPage, 1));
newState = {serverPage: 1, atEnd: false, displayItems: []};
} else if (this.props.entries && this.props.entries.length > 0 && nextProps.entries.length === 0 && this.state.serverPage > 1) {
this.props.dispatch(this.props.fetchAction.trigger(...this.props.fetchParams, this.props.perPage, this.state.serverPage - 1));
_.extend(newState, {
serverPage: this.state.serverPage - 1,
Expand All @@ -61,10 +64,6 @@ export default class ServerSideTable extends SimpleTable {
}

componentWillReceiveProps(nextProps) {
if (this.didFetchParamsUpdate(nextProps)) {
this.setState({serverPage: 1});
this.props.dispatch(nextProps.fetchAction.trigger(...nextProps.fetchParams, nextProps.perPage, 1));
}
this.updateDisplay(nextProps);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const TaskHistoryTable = ({requestId, tasksAPI}) => {
let maybeSearchButton;
if (tasks.length) {
maybeSearchButton = (
<Link to={`request/${requestId}/taskSearch`}>
<Link to={`request/${requestId}/task-search`}>
<Button bsStyle="primary">
<Glyphicon glyph="search" aria-hidden="true" /><span> Search</span>
</Button>
Expand Down Expand Up @@ -56,7 +56,7 @@ const TaskHistoryTable = ({requestId, tasksAPI}) => {
<td><Link to={`request/${data.taskId.requestId}/deploy/${data.taskId.deployId}`}>{data.taskId.deployId}</Link></td>
<td>{Utils.timestampFromNow(data.taskId.startedAt)}</td>
<td>{Utils.timestampFromNow(data.updatedAt)}</td>
<td className="actions-column"><Link to={`request/${data.taskId.requestId}/tail/${config.finishedTaskLogPath}?taskIds=${data.taskId.id}`} title="Log">&middot;&middot;&middot;</Link></td>
<td className="actions-column"><Link to={`request/${data.taskId.requestId}/tail/${config.finishedTaskLogPath}?taskIds=${data.taskId.id}`} title="Log"><Glyphicon glyph="file" /></Link></td>
<td className="actions-column"><JSONButton object={data}>{'{ }'}</JSONButton></td>
</tr>
);
Expand Down
75 changes: 51 additions & 24 deletions SingularityUI/app/components/taskSearch/TaskSearch.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { Glyphicon } from 'react-bootstrap';
import { Glyphicon, Label } from 'react-bootstrap';
import rootComponent from '../../rootComponent';
import classNames from 'classnames';
import { FetchTaskSearchParams } from '../../actions/api/history';
import { UpdateFilter } from '../../actions/ui/TaskSearch';

import Breadcrumbs from '../common/Breadcrumbs';
import TaskSearchFilters from './TaskSearchFilters';
import JSONButton from '../common/JSONButton';
import ServerSideTable from '../common/ServerSideTable';
import Utils from '../../utils';

Expand All @@ -23,19 +22,18 @@ class TaskSearch extends React.Component {
taskHistory: React.PropTypes.array,
filter: React.PropTypes.shape({
count: React.PropTypes.number,
page: React.PropTypes.number
requestId: React.PropTypes.string,
deployId: React.PropTypes.string,
host: React.PropTypes.string,
startedAfter: React.PropTypes.number,
startedBefore: React.PropTypes.number,
lastTaskStatus: React.PropTypes.string
}),
params: React.PropTypes.shape({
requestId: React.PropTypes.string
})
}

componentWillMount() {
const filter = _.extend({}, { requestId: this.props.params.requestId }, this.props.filter);
this.props.fetchTaskHistory(INITIAL_TASKS_PER_PAGE, 1, filter);
this.props.updateFilter(filter);
}

setCount(count) {
this.props.updateFilter(_.extend({}, this.props.filter, {count}));
}
Expand All @@ -49,11 +47,32 @@ class TaskSearch extends React.Component {

handleSearch(filter) {
const count = this.props.filter.count || INITIAL_TASKS_PER_PAGE;
const page = this.props.filter.page || 1;
const page = 1;
const newFilter = _.extend({}, {requestId: this.props.params.requestId}, _.omit(filter, (value) => !value), {count, page});
this.props.updateFilter(newFilter);
}

renderTag(field, value) {
return (
<Label>
{field}: <b>{value}</b>
</Label>
);
}

renderTags() {
const {requestId, deployId, host, startedAfter, startedBefore, lastTaskStatus} = this.props.filter;
return (
<div>
{requestId && !this.props.params.requestId && this.renderTag('Request ID', requestId)}{' '}
{deployId && this.renderTag('Deploy ID', deployId)}{' '}
{host && this.renderTag('Host', host)}{' '}
{startedAfter && this.renderTag('Started After', Utils.absoluteTimestamp(parseInt(startedAfter, 10)))}{' '}
{startedBefore && this.renderTag('Started Before', Utils.absoluteTimestamp(parseInt(startedBefore, 10)))}{' '}
{lastTaskStatus && this.renderTag('Last Task Status', Utils.humanizeText(lastTaskStatus))}
</div>);
}

renderTableRow(data, key) {
return (
<tr key={key}>
Expand All @@ -69,8 +88,7 @@ class TaskSearch extends React.Component {
<td>{Utils.timestampFromNow(data.taskId.startedAt)}</td>
<td>{Utils.timestampFromNow(data.updatedAt)}</td>
<td className="actions-column">
<Link to={`task/${data.taskId.id}/tail/${config.finishedTaskLogPath}`}>···</Link>
<JSONButton object={data}>{'{ }'}</JSONButton>
<Link to={`task/${data.taskId.id}/tail/${config.finishedTaskLogPath}`}><Glyphicon glyph="file" /></Link>
</td>
</tr>
);
Expand All @@ -91,14 +109,12 @@ class TaskSearch extends React.Component {
}

renderPageOptions() {
return this.props.taskHistory.length && (
<div className="row">
<div className="pull-right count-options">
Results per page:
<a className={classNames({inactive: this.isCurrentCount(5)})} onClick={() => this.setCount(5)}>5</a>
<a className={classNames({inactive: this.isCurrentCount(10)})} onClick={() => this.setCount(10)}>10</a>
<a className={classNames({inactive: this.isCurrentCount(25)})} onClick={() => this.setCount(25)}>25</a>
</div>
return this.props.taskHistory.length !== 0 && (
<div className="pull-right count-options">
Results per page:
<a className={classNames({inactive: this.isCurrentCount(5)})} onClick={() => this.setCount(5)}>5</a>
<a className={classNames({inactive: this.isCurrentCount(10)})} onClick={() => this.setCount(10)}>10</a>
<a className={classNames({inactive: this.isCurrentCount(25)})} onClick={() => this.setCount(25)}>25</a>
</div>
);
}
Expand All @@ -107,19 +123,22 @@ class TaskSearch extends React.Component {
return (
<div>
{this.renderBreadcrumbs()}
<h1 className="inline-header">{!this.props.params.requestId && 'Global '}Historical Tasks </h1>
<h1 className="inline-header">Task Search</h1>
{this.props.params.requestId && <h3 className="inline-header" style={{marginLeft: '10px'}}>for {this.props.params.requestId}</h3>}
<h2>Search Parameters</h2>
<TaskSearchFilters requestId={this.props.params.requestId} onSearch={(filter) => this.handleSearch(filter)} />
{this.renderPageOptions()}
<div className="row">
{this.renderTags()}
{this.renderPageOptions()}
</div>
<ServerSideTable
emptyMessage="No matching tasks"
entries={this.props.taskHistory}
paginate={true}
perPage={this.props.filter.count || INITIAL_TASKS_PER_PAGE}
fetchAction={FetchTaskSearchParams}
fetchParams={[this.props.filter]}
headers={['', 'Request ID', 'Deploy ID', 'Host', 'Last Status', 'Started', 'Updated', '']}
headers={['', 'Request ID', 'Deploy ID', 'Host', 'Last Status', 'Started', 'Updated', 'Logs']}
renderTableRow={(...args) => this.renderTableRow(...args)}
/>
</div>
Expand All @@ -141,4 +160,12 @@ function mapDispatchToProps(dispatch) {
};
}

export default connect(mapStateToProps, mapDispatchToProps)(rootComponent(TaskSearch, 'Task Search'));
function refresh(props) {
const promises = [];
const filter = _.extend({}, { requestId: props.params.requestId }, props.filter);
promises.push(props.fetchTaskHistory(INITIAL_TASKS_PER_PAGE, 1, filter));
promises.push(props.updateFilter(filter));
return Promise.all(promises);
}

export default connect(mapStateToProps, mapDispatchToProps)(rootComponent(TaskSearch, 'Task Search', refresh, false));
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class TaskSearchFilters extends React.Component {
</div>
</div>
<Button type="submit" bsStyle="primary" className="pull-right" disabled={!this.props.valid}>Submit</Button>
<Button type="button" bsStyle="default" className="pull-right" onClick={() => this.props.resetForm()}>Clear</Button>
<Button type="button" bsStyle="default" className="pull-right" onClick={() => this.props.resetForm()}>Clear Form</Button>
</form>
</Panel>
);
Expand Down
4 changes: 2 additions & 2 deletions SingularityUI/app/router.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const AppRouter = (props) => {
<Route path="requests(/:state)(/:subFilter)(/:searchFilter)" component={RequestsPage} />
<Route path="request">
<Route path=":requestId" component={RequestDetailPage} />
<Route path=":requestId/taskSearch" component={TaskSearch} />
<Route path=":requestId/task-search" component={TaskSearch} />
<Route path=":requestId/deploy" component={NewDeployForm} />
<Route path=":requestId/deploy/:deployId" component={DeployDetail} store={props.store} />
<Route path=":requestId/tail/**" component={AggregateTail} />
Expand All @@ -51,7 +51,7 @@ const AppRouter = (props) => {
<Route path="racks(/:state)" component={Racks} />
<Route path="slaves(/:state)" component={Slaves} />
<Route path="webhooks" component={Webhooks} />
<Route path="taskSearch" component={TaskSearch} />
<Route path="task-search" component={TaskSearch} />
<Route path="*" component={NotFound} />
</Route>
</Router>
Expand Down