From: Stas Vilchik Date: Thu, 30 Aug 2018 12:10:47 +0000 (+0200) Subject: rewrite the rest of the background tasks app in ts X-Git-Tag: 7.5~376 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a8fdcdc6539a65afea4039bc05b16d70a6603811;p=sonarqube.git rewrite the rest of the background tasks app in ts --- diff --git a/server/sonar-web/src/main/js/api/ce.ts b/server/sonar-web/src/main/js/api/ce.ts index 3d61fbc6589..3762ec76e1c 100644 --- a/server/sonar-web/src/main/js/api/ce.ts +++ b/server/sonar-web/src/main/js/api/ce.ts @@ -21,11 +21,13 @@ import { getJSON, post, RequestData } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; import { Task } from '../app/types'; -export function getActivity(data: RequestData): Promise { +export function getActivity(data: RequestData): Promise<{ tasks: Task[] }> { return getJSON('/api/ce/activity', data); } -export function getStatus(componentId?: string): Promise { +export function getStatus( + componentId?: string +): Promise<{ failing: number; inProgress: number; pending: number }> { const data = {}; if (componentId) { Object.assign(data, { componentId }); @@ -51,7 +53,7 @@ export function getTasksForComponent( return getJSON('/api/ce/component', { componentKey }).catch(throwGlobalError); } -export function getTypes(): Promise { +export function getTypes(): Promise { return getJSON('/api/ce/task_types').then(r => r.taskTypes); } diff --git a/server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-test.tsx b/server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-test.tsx new file mode 100644 index 00000000000..8dfe21635f4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-test.tsx @@ -0,0 +1,216 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import Stats from '../components/Stats'; +import Search from '../components/Search'; +import { STATUSES, CURRENTS, DEBOUNCE_DELAY, DEFAULT_FILTERS } from '../constants'; +import { formatDuration } from '../utils'; +import { click } from '../../../helpers/testUtils'; + +const stub = jest.fn(); + +describe('Constants', () => { + it('should have STATUSES', () => { + expect(Object.keys(STATUSES).length).toBe(7); + }); + + it('should have CURRENTS', () => { + expect(Object.keys(CURRENTS).length).toBe(2); + }); +}); + +describe('Search', () => { + const defaultProps: Search['props'] = { + ...DEFAULT_FILTERS, + loading: false, + types: [], + onFilterUpdate: () => true, + onReload: () => true, + maxExecutedAt: undefined, + minSubmittedAt: undefined + }; + + it('should render search form', () => { + const component = shallow(); + expect(component.find('SearchBox').exists()).toBeTruthy(); + }); + + it('should not render search form', () => { + const component = shallow(); + expect(component.find('SearchBox').exists()).toBeFalsy(); + }); + + it('should search', done => { + const searchSpy = jest.fn(); + const component = shallow(); + const searchInput = component.find('SearchBox'); + searchInput.prop('onChange')('some search query'); + setTimeout(() => { + expect(searchSpy).toBeCalledWith({ query: 'some search query' }); + done(); + }, DEBOUNCE_DELAY); + }); + + it('should reload', () => { + const reloadSpy = jest.fn(); + const component = shallow(); + const reloadButton = component.find('.js-reload'); + expect(reloadSpy).not.toBeCalled(); + click(reloadButton); + expect(reloadSpy).toBeCalled(); + }); +}); + +describe('Stats', () => { + describe('Pending', () => { + it('should show zero pending', () => { + const result = shallow( + + ); + expect(result.find('.js-pending-count').text()).toContain('0'); + }); + + it('should show 5 pending', () => { + const result = shallow( + + ); + expect(result.find('.js-pending-count').text()).toContain('5'); + }); + + it('should not show cancel pending button', () => { + const result = shallow( + + ); + expect(result.find('.js-cancel-pending').length).toBe(0); + }); + + it('should show cancel pending button', () => { + const result = shallow( + + ); + expect(result.find('.js-cancel-pending').length).toBe(1); + }); + + it('should trigger cancelling pending', () => { + const spy = jest.fn(); + const result = shallow( + + ); + expect(spy).not.toBeCalled(); + click(result.find('.js-cancel-pending')); + expect(spy).toBeCalled(); + }); + }); + + describe('Failures', () => { + it('should show zero failures', () => { + const result = shallow( + + ); + expect(result.find('.js-failures-count').text()).toContain('0'); + }); + + it('should show 5 failures', () => { + const result = shallow( + + ); + expect(result.find('.js-failures-count').text()).toContain('5'); + }); + + it('should not show link to failures', () => { + const result = shallow( + + ); + expect(result.find('.js-failures-count').is('a')).toBeFalsy(); + }); + + it('should show link to failures', () => { + const result = shallow( + + ); + expect(result.find('.js-failures-count').is('a')).toBeTruthy(); + }); + + it('should trigger filtering failures', () => { + const spy = jest.fn(); + const result = shallow( + + ); + expect(spy).not.toBeCalled(); + click(result.find('.js-failures-count')); + expect(spy).toBeCalled(); + }); + }); +}); + +describe('Helpers', () => { + describe('#formatDuration()', () => { + it('should format 173ms', () => { + expect(formatDuration(173)).toBe('173ms'); + }); + + it('should format 999ms', () => { + expect(formatDuration(999)).toBe('999ms'); + }); + + it('should format 1s 0ms', () => { + expect(formatDuration(1000)).toBe('1.0s'); + }); + + it('should format 1s 1ms', () => { + expect(formatDuration(1001)).toBe('1.1s'); + }); + + it('should format 1s 501ms', () => { + expect(formatDuration(1501)).toBe('1.501s'); + }); + + it('should format 59s', () => { + expect(formatDuration(59000)).toBe('59s'); + }); + + it('should format 1min 0s', () => { + expect(formatDuration(60000)).toBe('1min 0s'); + }); + + it('should format 1min 2s', () => { + expect(formatDuration(62757)).toBe('1min 2s'); + }); + + it('should format 3min 44s', () => { + expect(formatDuration(224567)).toBe('3min 44s'); + }); + + it('should format 1h 20m', () => { + expect(formatDuration(80 * 60 * 1000)).toBe('1h 20min'); + }); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js b/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js deleted file mode 100644 index f4f686fd287..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/__tests__/background-tasks-test.js +++ /dev/null @@ -1,214 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import React from 'react'; -import { shallow } from 'enzyme'; -import Stats from '../components/Stats'; -import Search from '../components/Search'; -import { STATUSES, CURRENTS, DEBOUNCE_DELAY, DEFAULT_FILTERS } from '../constants'; -import { formatDuration } from '../utils'; -import { change, click } from '../../../helpers/testUtils'; - -const stub = jest.fn(); - -describe('Constants', () => { - it('should have STATUSES', () => { - expect(Object.keys(STATUSES).length).toBe(7); - }); - - it('should have CURRENTS', () => { - expect(Object.keys(CURRENTS).length).toBe(2); - }); -}); - -describe('Search', () => { - const defaultProps = { - ...DEFAULT_FILTERS, - loading: false, - types: [], - onFilterUpdate: () => true, - onReload: () => true - }; - - it('should render search form', () => { - const component = shallow(); - expect(component.find('SearchBox').exists()).toBeTruthy(); - }); - - it('should not render search form', () => { - const component = shallow(); - expect(component.find('SearchBox').exists()).toBeFalsy(); - }); - - it('should search', done => { - const searchSpy = jest.fn(); - const component = shallow(); - const searchInput = component.find('SearchBox'); - searchInput.prop('onChange')('some search query'); - setTimeout(() => { - expect(searchSpy).toBeCalledWith({ query: 'some search query' }); - done(); - }, DEBOUNCE_DELAY); - }); - - it('should reload', () => { - const reloadSpy = jest.fn(); - const component = shallow(); - const reloadButton = component.find('.js-reload'); - expect(reloadSpy).not.toBeCalled(); - click(reloadButton); - expect(reloadSpy).toBeCalled(); - }); -}); - -describe('Stats', () => { - describe('Pending', () => { - it('should show zero pending', () => { - const result = shallow( - - ); - expect(result.find('.js-pending-count').text()).toContain('0'); - }); - - it('should show 5 pending', () => { - const result = shallow( - - ); - expect(result.find('.js-pending-count').text()).toContain('5'); - }); - - it('should not show cancel pending button', () => { - const result = shallow( - - ); - expect(result.find('.js-cancel-pending').length).toBe(0); - }); - - it('should show cancel pending button', () => { - const result = shallow( - - ); - expect(result.find('.js-cancel-pending').length).toBe(1); - }); - - it('should trigger cancelling pending', () => { - const spy = jest.fn(); - const result = shallow( - - ); - expect(spy).not.toBeCalled(); - click(result.find('.js-cancel-pending')); - expect(spy).toBeCalled(); - }); - }); - - describe('Failures', () => { - it('should show zero failures', () => { - const result = shallow( - - ); - expect(result.find('.js-failures-count').text()).toContain('0'); - }); - - it('should show 5 failures', () => { - const result = shallow( - - ); - expect(result.find('.js-failures-count').text()).toContain('5'); - }); - - it('should not show link to failures', () => { - const result = shallow( - - ); - expect(result.find('.js-failures-count').is('a')).toBeFalsy(); - }); - - it('should show link to failures', () => { - const result = shallow( - - ); - expect(result.find('.js-failures-count').is('a')).toBeTruthy(); - }); - - it('should trigger filtering failures', () => { - const spy = jest.fn(); - const result = shallow( - - ); - expect(spy).not.toBeCalled(); - click(result.find('.js-failures-count')); - expect(spy).toBeCalled(); - }); - }); -}); - -describe('Helpers', () => { - describe('#formatDuration()', () => { - it('should format 173ms', () => { - expect(formatDuration(173)).toBe('173ms'); - }); - - it('should format 999ms', () => { - expect(formatDuration(999)).toBe('999ms'); - }); - - it('should format 1s 0ms', () => { - expect(formatDuration(1000)).toBe('1.0s'); - }); - - it('should format 1s 1ms', () => { - expect(formatDuration(1001)).toBe('1.1s'); - }); - - it('should format 1s 501ms', () => { - expect(formatDuration(1501)).toBe('1.501s'); - }); - - it('should format 59s', () => { - expect(formatDuration(59000)).toBe('59s'); - }); - - it('should format 1min 0s', () => { - expect(formatDuration(60000)).toBe('1min 0s'); - }); - - it('should format 1min 2s', () => { - expect(formatDuration(62757)).toBe('1min 2s'); - }); - - it('should format 3min 44s', () => { - expect(formatDuration(224567)).toBe('3min 44s'); - }); - - it('should format 1h 20m', () => { - expect(formatDuration(80 * 60 * 1000)).toBe('1h 20min'); - }); - }); -}); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js deleted file mode 100644 index 50b47f89b62..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.js +++ /dev/null @@ -1,275 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 PropTypes from 'prop-types'; -import Helmet from 'react-helmet'; -import { debounce, uniq } from 'lodash'; -import { connect } from 'react-redux'; -import Header from './Header'; -import Footer from './Footer'; -import StatsContainer from './StatsContainer'; -import Search from './Search'; -import Tasks from './Tasks'; -import { DEFAULT_FILTERS, DEBOUNCE_DELAY, STATUSES, CURRENTS } from '../constants'; -import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; -import { - getTypes, - getActivity, - getStatus, - cancelAllTasks, - cancelTask as cancelTaskAPI -} from '../../../api/ce'; -import { updateTask, mapFiltersToParameters } from '../utils'; -import { fetchOrganizations } from '../../../store/rootActions'; -import { translate } from '../../../helpers/l10n'; -import { parseAsDate } from '../../../helpers/query'; -import { toShortNotSoISOString } from '../../../helpers/dates'; -import '../background-tasks.css'; - -/*:: -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.PureComponent { - /*:: loadTasksDebounced: Function; */ - /*:: mounted: boolean; */ - /*:: props: Props; */ - - static contextTypes = { - router: 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(); - }); - } - - 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]; - } - }); - - if (nextQuery.minSubmittedAt) { - nextQuery.minSubmittedAt = toShortNotSoISOString(nextQuery.minSubmittedAt); - } - - if (nextQuery.maxExecutedAt) { - nextQuery.maxExecutedAt = toShortNotSoISOString(nextQuery.maxExecutedAt); - } - - this.context.router.push({ - pathname: this.props.location.pathname, - query: nextQuery - }); - } - - handleCancelTask(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) { - 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 = parseAsDate(this.props.location.query.minSubmittedAt); - const maxExecutedAt = parseAsDate(this.props.location.query.maxExecutedAt); - const query = this.props.location.query.query || ''; - - return ( -
- - -
- - - - - - - -
-
- ); - } -} - -const mapDispatchToProps = { fetchOrganizations }; - -export default connect( - null, - mapDispatchToProps -)(BackgroundTasksApp); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx new file mode 100644 index 00000000000..9dcbfda40c0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx @@ -0,0 +1,275 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import Helmet from 'react-helmet'; +import { debounce, uniq } from 'lodash'; +import { connect } from 'react-redux'; +import { InjectedRouter } from 'react-router'; +import { Location } from 'history'; +import Header from './Header'; +import Footer from './Footer'; +import StatsContainer from './StatsContainer'; +import Search from './Search'; +import Tasks from './Tasks'; +import { DEFAULT_FILTERS, DEBOUNCE_DELAY, STATUSES, CURRENTS } from '../constants'; +import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; +import { + getTypes, + getActivity, + getStatus, + cancelAllTasks, + cancelTask as cancelTaskAPI +} from '../../../api/ce'; +import { updateTask, mapFiltersToParameters, Query } from '../utils'; +import { fetchOrganizations } from '../../../store/rootActions'; +import { translate } from '../../../helpers/l10n'; +import { parseAsDate } from '../../../helpers/query'; +import { toShortNotSoISOString } from '../../../helpers/dates'; +import '../background-tasks.css'; +import { Task } from '../../../app/types'; + +interface Props { + component?: { id: string }; + fetchOrganizations: (keys: string[]) => void; + location: Location; + router: Pick; +} + +interface State { + loading: boolean; + tasks: Task[]; + types?: string[]; + query: string; + pendingCount: number; + failingCount: number; +} + +class BackgroundTasksApp extends React.PureComponent { + loadTasksDebounced: () => void; + mounted = false; + + constructor(props: Props) { + super(props); + this.state = { + failingCount: 0, + loading: true, + pendingCount: 0, + query: '', + tasks: [] + }; + this.loadTasksDebounced = debounce(this.loadTasks, DEBOUNCE_DELAY); + } + + componentDidMount() { + this.mounted = true; + + getTypes().then( + types => { + this.setState({ types }); + this.loadTasks(); + }, + () => {} + ); + } + + componentDidUpdate(prevProps: Props) { + if ( + prevProps.component !== this.props.component || + prevProps.location !== this.props.location + ) { + this.loadTasksDebounced(); + } + } + + componentWillUnmount() { + this.mounted = false; + } + + stopLoading = () => { + if (this.mounted) { + this.setState({ loading: 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; + + 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 + }); + } + }, this.stopLoading); + }; + + handleFilterUpdate = (nextState: Partial) => { + const nextQuery = { ...this.props.location.query, ...nextState }; + + // remove defaults + Object.keys(DEFAULT_FILTERS).forEach((key: keyof typeof DEFAULT_FILTERS) => { + if (nextQuery[key] === DEFAULT_FILTERS[key]) { + delete nextQuery[key]; + } + }); + + if (nextQuery.minSubmittedAt) { + nextQuery.minSubmittedAt = toShortNotSoISOString(nextQuery.minSubmittedAt); + } + + if (nextQuery.maxExecutedAt) { + nextQuery.maxExecutedAt = toShortNotSoISOString(nextQuery.maxExecutedAt); + } + + this.props.router.push({ + pathname: this.props.location.pathname, + query: nextQuery + }); + }; + + handleCancelTask = (task: Task) => { + this.setState({ loading: true }); + + cancelTaskAPI(task.id).then(nextTask => { + if (this.mounted) { + this.setState(state => ({ + tasks: updateTask(state.tasks, nextTask), + loading: false + })); + } + }, this.stopLoading); + }; + + 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(); + } + }, this.stopLoading); + } + + 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 = parseAsDate(this.props.location.query.minSubmittedAt); + const maxExecutedAt = parseAsDate(this.props.location.query.maxExecutedAt); + const query = this.props.location.query.query || ''; + + return ( +
+ + +
+ + + + + + + +
+
+ ); + } +} + +const mapDispatchToProps = { fetchOrganizations }; + +export default connect( + null, + mapDispatchToProps +)(BackgroundTasksApp); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js deleted file mode 100644 index 3e02fcc974d..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Search.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 PropTypes from 'prop-types'; -import StatusFilter from './StatusFilter'; -import TypesFilter from './TypesFilter'; -import CurrentsFilter from './CurrentsFilter'; -import DateFilter from './DateFilter'; -import { DEFAULT_FILTERS } from '../constants'; -import SearchBox from '../../../components/controls/SearchBox'; -import { Button } from '../../../components/ui/buttons'; -import { translate } from '../../../helpers/l10n'; - -export default class Search extends React.PureComponent { - static propTypes = { - loading: PropTypes.bool.isRequired, - status: PropTypes.any.isRequired, - taskType: PropTypes.any.isRequired, - currents: PropTypes.any.isRequired, - query: PropTypes.string.isRequired, - onFilterUpdate: PropTypes.func.isRequired, - onReload: PropTypes.func.isRequired - }; - - handleStatusChange = (status /*: string */) => { - this.props.onFilterUpdate({ status }); - }; - - handleTypeChange = (taskType /*: string */) => { - this.props.onFilterUpdate({ taskType }); - }; - - handleCurrentsChange = (currents /*: string */) => { - this.props.onFilterUpdate({ currents }); - }; - - handleDateChange = (date /*: { maxExecutedAt?: Date; minSubmittedAt?: Date } */) => { - this.props.onFilterUpdate(date); - }; - - handleQueryChange = (query /*: string */) => { - this.props.onFilterUpdate({ query }); - }; - - handleReset = () => { - this.props.onFilterUpdate(DEFAULT_FILTERS); - }; - - renderSearchBox() { - const { component, query } = this.props; - - if (component) { - // do not render search form on the project-level page - return null; - } - - return ( -
  • - -
  • - ); - } - - render() { - const { - loading, - component, - types, - status, - taskType, - currents, - minSubmittedAt, - maxExecutedAt - } = this.props; - - return ( -
    -
      -
    • -
      {translate('status')}
      - -
    • - {types.length > 1 && ( -
    • -
      {translate('type')}
      - -
    • - )} - {!component && ( -
    • -
      - {translate('background_tasks.currents_filter.ONLY_CURRENTS')} -
      - -
    • - )} -
    • -
      {translate('date')}
      - -
    • - - {this.renderSearchBox()} - -
    • - {' '} - -
    • -
    -
    - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Search.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Search.tsx new file mode 100644 index 00000000000..e7b2687ee4f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Search.tsx @@ -0,0 +1,145 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import StatusFilter from './StatusFilter'; +import TypesFilter from './TypesFilter'; +import CurrentsFilter from './CurrentsFilter'; +import DateFilter from './DateFilter'; +import { DEFAULT_FILTERS } from '../constants'; +import SearchBox from '../../../components/controls/SearchBox'; +import { Button } from '../../../components/ui/buttons'; +import { translate } from '../../../helpers/l10n'; +import { Query } from '../utils'; + +interface Props { + component?: unknown; + currents: string; + loading: boolean; + onFilterUpdate: (changes: Partial) => void; + onReload: () => void; + query: string; + status: string; + taskType: string; + maxExecutedAt: Date | undefined; + minSubmittedAt: Date | undefined; + types: string[]; +} + +export default class Search extends React.PureComponent { + handleStatusChange = (status: string) => { + this.props.onFilterUpdate({ status }); + }; + + handleTypeChange = (taskType: string) => { + this.props.onFilterUpdate({ taskType }); + }; + + handleCurrentsChange = (currents: string) => { + this.props.onFilterUpdate({ currents }); + }; + + handleDateChange = (date: { maxExecutedAt?: Date; minSubmittedAt?: Date }) => { + this.props.onFilterUpdate(date); + }; + + handleQueryChange = (query: string) => { + this.props.onFilterUpdate({ query }); + }; + + handleReset = () => { + this.props.onFilterUpdate(DEFAULT_FILTERS); + }; + + renderSearchBox() { + const { component, query } = this.props; + + if (component) { + // do not render search form on the project-level page + return null; + } + + return ( +
  • + +
  • + ); + } + + render() { + const { + loading, + component, + types, + status, + taskType, + currents, + minSubmittedAt, + maxExecutedAt + } = this.props; + + return ( +
    +
      +
    • +
      {translate('status')}
      + +
    • + {types.length > 1 && ( +
    • +
      {translate('type')}
      + +
    • + )} + {!component && ( +
    • +
      + {translate('background_tasks.currents_filter.ONLY_CURRENTS')} +
      + +
    • + )} +
    • +
      {translate('date')}
      + +
    • + + {this.renderSearchBox()} + +
    • + {' '} + +
    • +
    +
    + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js deleted file mode 100644 index 5e47a33406d..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 Tooltip from '../../../components/controls/Tooltip'; -import { DeleteButton } from '../../../components/ui/buttons'; -import { translate } from '../../../helpers/l10n'; - -/*:: -type Props = { - failingCount: number, - isSystemAdmin?: boolean, - pendingCount: number, - onShowFailing: () => void, - onCancelAllPending: () => void -}; -*/ - -/*:: -type State = Object; -*/ - -export default class Stats extends React.PureComponent { - /*:: props: Props; */ - /*:: state: State; */ - - handleShowFailing = (event /*: Object */) => { - event.preventDefault(); - event.currentTarget.blur(); - this.props.onShowFailing(); - }; - - renderPending() { - if (this.props.pendingCount == null) { - return null; - } - if (this.props.pendingCount > 0) { - return ( - - {this.props.pendingCount} -   - {translate('background_tasks.pending')} - {this.props.isSystemAdmin && ( - - - - )} - - ); - } else { - return ( - - {this.props.pendingCount} -   - {translate('background_tasks.pending')} - - ); - } - } - - renderFailures() { - if (this.props.failingCount == null) { - return null; - } - - if (this.props.component) { - return null; - } - - if (this.props.failingCount > 0) { - return ( - - - - {this.props.failingCount} - - -   - {translate('background_tasks.failures')} - - ); - } else { - return ( - - - {this.props.failingCount} - -   - {translate('background_tasks.failures')} - - ); - } - } - - render() { - return ( -
    - {this.renderPending()} - {this.renderFailures()} -
    - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.tsx new file mode 100644 index 00000000000..31d9365d975 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Stats.tsx @@ -0,0 +1,117 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import Tooltip from '../../../components/controls/Tooltip'; +import { DeleteButton } from '../../../components/ui/buttons'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + component?: unknown; + failingCount?: number; + isSystemAdmin?: boolean; + pendingCount?: number; + onShowFailing: () => void; + onCancelAllPending: () => void; +} + +export default class Stats extends React.PureComponent { + handleShowFailing = (event: React.MouseEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + this.props.onShowFailing(); + }; + + renderPending() { + if (this.props.pendingCount === undefined) { + return null; + } + if (this.props.pendingCount > 0) { + return ( + + {this.props.pendingCount} +   + {translate('background_tasks.pending')} + {this.props.isSystemAdmin && ( + + + + )} + + ); + } else { + return ( + + {this.props.pendingCount} +   + {translate('background_tasks.pending')} + + ); + } + } + + renderFailures() { + if (this.props.failingCount === undefined) { + return null; + } + + if (this.props.component) { + return null; + } + + if (this.props.failingCount > 0) { + return ( + + + + {this.props.failingCount} + + +   + {translate('background_tasks.failures')} + + ); + } else { + return ( + + + {this.props.failingCount} + +   + {translate('background_tasks.failures')} + + ); + } + } + + render() { + return ( +
    + {this.renderPending()} + {this.renderFailures()} +
    + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/StatsContainer.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/StatsContainer.tsx index f4ab2473ec5..2cc54c8f6de 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/StatsContainer.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/StatsContainer.tsx @@ -25,4 +25,4 @@ const mapStateToProps = (state: Store) => ({ isSystemAdmin: !!getAppState(state).canAdmin }); -export default connect(mapStateToProps)(Stats as any); +export default connect(mapStateToProps)(Stats); diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Task.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Task.tsx index c51a705688b..7dd532d3f98 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Task.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Task.tsx @@ -29,7 +29,7 @@ import TaskSubmitter from './TaskSubmitter'; import { Task as TaskType } from '../../../app/types'; interface Props { - component?: {}; + component?: unknown; onCancelTask: (task: TaskType) => void; onFilterTask: (task: TaskType) => void; task: TaskType; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx index 119d8ac7d61..814611067c7 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.tsx @@ -32,7 +32,7 @@ const AnalysisWarningsModal = lazyLoad( ); interface Props { - component?: {}; + component?: unknown; onCancelTask: (task: Task) => void; onFilterTask: (task: Task) => void; task: Task; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js deleted file mode 100644 index 3d5f6bb7f1b..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 classNames from 'classnames'; -import Task from './Task'; -import { translate } from '../../../helpers/l10n'; - -/*:: -type Props = { - tasks: Array<*>, - component: Object, - loading: boolean, - onCancelTask: Function, - onFilterTask: Function -}; -*/ - -/*:: -type State = Object; -*/ - -export default class Tasks extends React.PureComponent { - /*:: props: Props; */ - /*:: state: State; */ - - render() { - const { tasks, component, loading, onCancelTask, onFilterTask } = this.props; - - const className = classNames('data zebra zebra-hover background-tasks', { - 'new-loading': loading - }); - - return ( -
    - - - - - - - - - - - - - - - - - {tasks.map((task, index, tasks) => ( - 0 ? tasks[index - 1] : undefined} - task={task} - tasks={tasks} - /> - ))} - -
    {translate('background_tasks.table.status')}{translate('background_tasks.table.task')}{translate('background_tasks.table.id')}{translate('background_tasks.table.submitter')} {translate('background_tasks.table.submitted')}{translate('background_tasks.table.started')}{translate('background_tasks.table.finished')}{translate('background_tasks.table.duration')} 
    -
    - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.tsx new file mode 100644 index 00000000000..db104a80984 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/Tasks.tsx @@ -0,0 +1,71 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import * as React from 'react'; +import * as classNames from 'classnames'; +import Task from './Task'; +import { Task as TaskType } from '../../../app/types'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + tasks: TaskType[]; + component?: unknown; + loading: boolean; + onCancelTask: (task: TaskType) => void; + onFilterTask: (task: TaskType) => void; +} + +export default function Tasks({ tasks, component, loading, onCancelTask, onFilterTask }: Props) { + const className = classNames('data zebra zebra-hover background-tasks', { + 'new-loading': loading + }); + + return ( +
    + + + + + + + + + + + + + + + + + {tasks.map((task, index, tasks) => ( + 0 ? tasks[index - 1] : undefined} + task={task} + /> + ))} + +
    {translate('background_tasks.table.status')}{translate('background_tasks.table.task')}{translate('background_tasks.table.id')}{translate('background_tasks.table.submitter')} {translate('background_tasks.table.submitted')}{translate('background_tasks.table.started')}{translate('background_tasks.table.finished')}{translate('background_tasks.table.duration')} 
    +
    + ); +} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/constants.js b/server/sonar-web/src/main/js/apps/background-tasks/constants.js deleted file mode 100644 index 5cee6f2068d..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/constants.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 */ -export const STATUSES = { - ALL: '__ALL__', - ALL_EXCEPT_PENDING: '__ALL_EXCEPT_PENDING__', - PENDING: 'PENDING', - IN_PROGRESS: 'IN_PROGRESS', - SUCCESS: 'SUCCESS', - FAILED: 'FAILED', - CANCELED: 'CANCELED' -}; - -export const ALL_TYPES = 'ALL_TYPES'; - -export const CURRENTS = { - ALL: '__ALL__', - ONLY_CURRENTS: 'CURRENTS' -}; - -export const DATE = { - ANY: 'ANY', - TODAY: 'TODAY', - CUSTOM: 'CUSTOM' -}; - -export const DEFAULT_FILTERS = { - status: STATUSES.ALL_EXCEPT_PENDING, - taskType: ALL_TYPES, - currents: CURRENTS.ALL, - minSubmittedAt: undefined, - maxExecutedAt: undefined, - query: '' -}; - -export const DATE_FORMAT = 'YYYY-MM-DD'; - -export const DEBOUNCE_DELAY = 250; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/constants.ts b/server/sonar-web/src/main/js/apps/background-tasks/constants.ts new file mode 100644 index 00000000000..df6bcf793f3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/constants.ts @@ -0,0 +1,56 @@ +import { Query } from './utils'; + +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +export const STATUSES = { + ALL: '__ALL__', + ALL_EXCEPT_PENDING: '__ALL_EXCEPT_PENDING__', + PENDING: 'PENDING', + IN_PROGRESS: 'IN_PROGRESS', + SUCCESS: 'SUCCESS', + FAILED: 'FAILED', + CANCELED: 'CANCELED' +}; + +export const ALL_TYPES = 'ALL_TYPES'; + +export const CURRENTS = { + ALL: '__ALL__', + ONLY_CURRENTS: 'CURRENTS' +}; + +export const DATE = { + ANY: 'ANY', + TODAY: 'TODAY', + CUSTOM: 'CUSTOM' +}; + +export const DEFAULT_FILTERS: Query = { + status: STATUSES.ALL_EXCEPT_PENDING, + taskType: ALL_TYPES, + currents: CURRENTS.ALL, + minSubmittedAt: undefined, + maxExecutedAt: undefined, + query: '' +}; + +export const DATE_FORMAT = 'YYYY-MM-DD'; + +export const DEBOUNCE_DELAY = 250; diff --git a/server/sonar-web/src/main/js/apps/background-tasks/utils.js b/server/sonar-web/src/main/js/apps/background-tasks/utils.js deleted file mode 100644 index a023e7176e6..00000000000 --- a/server/sonar-web/src/main/js/apps/background-tasks/utils.js +++ /dev/null @@ -1,105 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -import { STATUSES, ALL_TYPES, CURRENTS } from './constants'; -import { toShortNotSoISOString } from '../../helpers/dates'; - -export function updateTask(tasks, newTask) { - return tasks.map(task => (task.id === newTask.id ? newTask : task)); -} - -export function mapFiltersToParameters(filters /*: Object */ = {}) { - const parameters = {}; - - if (filters.status === STATUSES.ALL) { - parameters.status = [ - STATUSES.PENDING, - STATUSES.IN_PROGRESS, - STATUSES.SUCCESS, - STATUSES.FAILED, - STATUSES.CANCELED - ].join(); - } else if (filters.status === STATUSES.ALL_EXCEPT_PENDING) { - parameters.status = [ - STATUSES.IN_PROGRESS, - STATUSES.SUCCESS, - STATUSES.FAILED, - STATUSES.CANCELED - ].join(); - } else { - parameters.status = filters.status; - } - - if (filters.taskType !== ALL_TYPES) { - parameters.type = filters.taskType; - } - - if (filters.currents !== CURRENTS.ALL) { - parameters.onlyCurrents = true; - } - - if (filters.minSubmittedAt) { - parameters.minSubmittedAt = toShortNotSoISOString(filters.minSubmittedAt); - } - - if (filters.maxExecutedAt) { - parameters.maxExecutedAt = toShortNotSoISOString(filters.maxExecutedAt); - } - - if (filters.query) { - parameters.componentQuery = filters.query; - } - - if (filters.lastPage !== 1) { - parameters.p = filters.lastPage; - } - - return parameters; -} - -const ONE_SECOND = 1000; -const ONE_MINUTE = 60 * ONE_SECOND; -const ONE_HOUR = 60 * ONE_MINUTE; - -function format(int, suffix) { - return `${int}${suffix}`; -} - -export function formatDuration(value /*: ?number */) { - if (!value) { - return ''; - } - if (value < ONE_SECOND) { - return format(value, 'ms'); - } else if (value < ONE_SECOND * 10) { - const seconds = Math.floor(value / ONE_SECOND); - const ms = value - seconds * ONE_SECOND; - return seconds + '.' + format(ms, 's'); - } else if (value < ONE_MINUTE) { - const seconds = Math.floor(value / ONE_SECOND); - return format(seconds, 's'); - } else if (value < ONE_MINUTE * 10) { - const minutes = Math.floor(value / ONE_MINUTE); - const seconds = Math.floor((value - minutes * ONE_MINUTE) / ONE_SECOND); - return format(minutes, 'min') + ' ' + format(seconds, 's'); - } - const hours = Math.floor(value / ONE_HOUR); - const minutes = Math.floor((value - hours * ONE_HOUR) / ONE_MINUTE); - return format(hours, 'h') + ' ' + format(minutes, 'min'); -} diff --git a/server/sonar-web/src/main/js/apps/background-tasks/utils.ts b/server/sonar-web/src/main/js/apps/background-tasks/utils.ts new file mode 100644 index 00000000000..848e240109f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/background-tasks/utils.ts @@ -0,0 +1,111 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +import { STATUSES, CURRENTS, ALL_TYPES } from './constants'; +import { toShortNotSoISOString } from '../../helpers/dates'; +import { Task } from '../../app/types'; + +export interface Query { + currents: string; + maxExecutedAt?: Date; + minSubmittedAt?: Date; + query: string; + status: string; + taskType: string; +} + +export function updateTask(tasks: Task[], newTask: Task) { + return tasks.map(task => (task.id === newTask.id ? newTask : task)); +} + +export function mapFiltersToParameters(filters: Partial = {}) { + const parameters: any = {}; + + if (filters.status === STATUSES.ALL) { + parameters.status = [ + STATUSES.PENDING, + STATUSES.IN_PROGRESS, + STATUSES.SUCCESS, + STATUSES.FAILED, + STATUSES.CANCELED + ].join(); + } else if (filters.status === STATUSES.ALL_EXCEPT_PENDING) { + parameters.status = [ + STATUSES.IN_PROGRESS, + STATUSES.SUCCESS, + STATUSES.FAILED, + STATUSES.CANCELED + ].join(); + } else { + parameters.status = filters.status; + } + + if (filters.taskType !== ALL_TYPES) { + parameters.type = filters.taskType; + } + + if (filters.currents !== CURRENTS.ALL) { + parameters.onlyCurrents = true; + } + + if (filters.minSubmittedAt) { + parameters.minSubmittedAt = toShortNotSoISOString(filters.minSubmittedAt); + } + + if (filters.maxExecutedAt) { + parameters.maxExecutedAt = toShortNotSoISOString(filters.maxExecutedAt); + } + + if (filters.query) { + parameters.componentQuery = filters.query; + } + + return parameters; +} + +const ONE_SECOND = 1000; +const ONE_MINUTE = 60 * ONE_SECOND; +const ONE_HOUR = 60 * ONE_MINUTE; + +function format(int: number, suffix: string) { + return `${int}${suffix}`; +} + +export function formatDuration(value: number | undefined) { + if (!value) { + return ''; + } + if (value < ONE_SECOND) { + return format(value, 'ms'); + } else if (value < ONE_SECOND * 10) { + const seconds = Math.floor(value / ONE_SECOND); + const ms = value - seconds * ONE_SECOND; + return seconds + '.' + format(ms, 's'); + } else if (value < ONE_MINUTE) { + const seconds = Math.floor(value / ONE_SECOND); + return format(seconds, 's'); + } else if (value < ONE_MINUTE * 10) { + const minutes = Math.floor(value / ONE_MINUTE); + const seconds = Math.floor((value - minutes * ONE_MINUTE) / ONE_SECOND); + return format(minutes, 'min') + ' ' + format(seconds, 's'); + } + const hours = Math.floor(value / ONE_HOUR); + const minutes = Math.floor((value - hours * ONE_HOUR) / ONE_MINUTE); + return format(hours, 'h') + ' ' + format(minutes, 'min'); +}