/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // @flow import React from 'react'; import shallowCompare from 'react-addons-shallow-compare'; import debounce from 'lodash/debounce'; import uniq from 'lodash/uniq'; import { connect } from 'react-redux'; import { DEFAULT_FILTERS, DEBOUNCE_DELAY, STATUSES, CURRENTS } from './../constants'; import Header from './Header'; import Footer from './Footer'; import Stats from '../components/Stats'; import Search from '../components/Search'; import Tasks from '../components/Tasks'; import { getTypes, getActivity, getStatus, cancelAllTasks, cancelTask as cancelTaskAPI } from '../../../api/ce'; import { updateTask, mapFiltersToParameters } from '../utils'; import { Task } from '../types'; import { getComponent } from '../../../store/rootReducer'; import '../background-tasks.css'; import { fetchOrganizations } from '../../../store/rootActions'; type Props = { component: Object, location: Object, fetchOrganizations: (Array) => string }; type State = { loading: boolean, tasks: Array<*>, types?: Array<*>, query: string, pendingCount: number, failingCount: number }; class BackgroundTasksApp extends React.Component { loadTasksDebounced: Function; mounted: boolean; props: Props; static contextTypes = { router: React.PropTypes.object.isRequired }; state: State = { loading: true, tasks: [], // filters query: '', // stats pendingCount: 0, failingCount: 0 }; componentWillMount() { this.loadTasksDebounced = debounce(this.loadTasks.bind(this), DEBOUNCE_DELAY); } componentDidMount() { this.mounted = true; getTypes().then(types => { this.setState({ types }); this.loadTasks(); }); } shouldComponentUpdate(nextProps: Props, nextState: State) { return shallowCompare(this, nextProps, nextState); } componentDidUpdate(prevProps: Props) { if ( prevProps.component !== this.props.component || prevProps.location !== this.props.location ) { this.loadTasksDebounced(); } } componentWillUnmount() { this.mounted = false; } loadTasks() { this.setState({ loading: true }); const status = this.props.location.query.status || DEFAULT_FILTERS.status; const taskType = this.props.location.query.taskType || DEFAULT_FILTERS.taskType; const currents = this.props.location.query.currents || DEFAULT_FILTERS.currents; const minSubmittedAt = this.props.location.query.minSubmittedAt || DEFAULT_FILTERS.minSubmittedAt; const maxExecutedAt = this.props.location.query.maxExecutedAt || DEFAULT_FILTERS.maxExecutedAt; const query = this.props.location.query.query || DEFAULT_FILTERS.query; const filters = { status, taskType, currents, minSubmittedAt, maxExecutedAt, query }; const parameters: Object = mapFiltersToParameters(filters); if (this.props.component) { parameters.componentId = this.props.component.id; } Promise.all([getActivity(parameters), getStatus(parameters.componentId)]).then(responses => { if (this.mounted) { const [activity, status] = responses; const tasks = activity.tasks; const pendingCount = status.pending; const failingCount = status.failing; const organizations = uniq(tasks.map(task => task.organization).filter(o => o)); this.props.fetchOrganizations(organizations); this.setState({ tasks, pendingCount, failingCount, loading: false }); } }); } handleFilterUpdate(nextState: Object) { const nextQuery = { ...this.props.location.query, ...nextState }; // remove defaults Object.keys(DEFAULT_FILTERS).forEach(key => { if (nextQuery[key] === DEFAULT_FILTERS[key]) { delete nextQuery[key]; } }); this.context.router.push({ pathname: this.props.location.pathname, query: nextQuery }); } handleCancelTask(task: Task) { this.setState({ loading: true }); cancelTaskAPI(task.id).then(nextTask => { if (this.mounted) { const tasks = updateTask(this.state.tasks, nextTask); this.setState({ tasks, loading: false }); } }); } handleFilterTask(task: Task) { this.handleFilterUpdate({ query: task.componentKey }); } handleShowFailing() { this.handleFilterUpdate({ ...DEFAULT_FILTERS, status: STATUSES.FAILED, currents: CURRENTS.ONLY_CURRENTS }); } handleCancelAllPending() { this.setState({ loading: true }); cancelAllTasks().then(() => { if (this.mounted) { this.loadTasks(); } }); } render() { const { component } = this.props; const { loading, types, tasks, pendingCount, failingCount } = this.state; if (!types) { return (
); } const status = this.props.location.query.status || DEFAULT_FILTERS.status; const taskType = this.props.location.query.taskType || DEFAULT_FILTERS.taskType; const currents = this.props.location.query.currents || DEFAULT_FILTERS.currents; const minSubmittedAt = this.props.location.query.minSubmittedAt || ''; const maxExecutedAt = this.props.location.query.maxExecutedAt || ''; const query = this.props.location.query.query || ''; return (
); } } const mapStateToProps = (state, ownProps) => ({ component: ownProps.location.query.id ? getComponent(state, ownProps.location.query.id) : undefined }); const mapDispatchToProps = { fetchOrganizations }; export default connect(mapStateToProps, mapDispatchToProps)(BackgroundTasksApp);