import { getJSON, post, RequestData } from '../helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';
+export interface PendingTask {
+ componentId: string;
+ componentKey: string;
+ componentName: string;
+ componentQualifier: string;
+ id: string;
+ logs: boolean;
+ organization: string;
+ status: string;
+ submittedAt: Date;
+ submitterLogin?: string;
+ type: string;
+}
+
+export interface Task extends PendingTask {
+ analysisId?: string;
+ errorMessage?: string;
+ executionTimeMs: number;
+ executedAt: Date;
+ hasErrorStacktrace: boolean;
+ hasScannerContext: boolean;
+ startedAt: Date;
+}
+
export function getActivity(data: RequestData): Promise<any> {
return getJSON('/api/ce/activity', data);
}
return post('/api/ce/cancel_all');
}
-export function getTasksForComponent(componentKey: string): Promise<any> {
+export function getTasksForComponent(
+ componentKey: string
+): Promise<{
+ queue: PendingTask[];
+ current: Task;
+}> {
return getJSON('/api/ce/component', { componentKey });
}
*/
import * as React from 'react';
import ComponentNavFavorite from './ComponentNavFavorite';
+import ComponentNavBranch from './ComponentNavBranch';
import ComponentNavBreadcrumbs from './ComponentNavBreadcrumbs';
import ComponentNavMeta from './ComponentNavMeta';
import ComponentNavMenu from './ComponentNavMenu';
-import ComponentNavBranch from './ComponentNavBranch';
+import ComponentNavBgTaskNotif from './ComponentNavBgTaskNotif';
import RecentHistory from '../../RecentHistory';
import { Branch, Component } from '../../../types';
import ContextNavBar from '../../../../components/nav/ContextNavBar';
-import { getTasksForComponent } from '../../../../api/ce';
+import { getTasksForComponent, PendingTask, Task } from '../../../../api/ce';
import { STATUSES } from '../../../../apps/background-tasks/constants';
import './ComponentNav.css';
}
interface State {
- isFailed?: boolean;
+ currentTask?: Task;
isInProgress?: boolean;
isPending?: boolean;
}
this.populateRecentHistory();
}
+ componentDidUpdate(prevProps: Props) {
+ if (this.props.component.key !== prevProps.component.key) {
+ this.loadStatus();
+ this.populateRecentHistory();
+ }
+ }
+
componentWillUnmount() {
this.mounted = false;
}
loadStatus = () => {
- getTasksForComponent(this.props.component.key).then((r: any) => {
+ getTasksForComponent(
+ this.props.component.key
+ ).then((r: { queue: PendingTask[]; current: Task }) => {
if (this.mounted) {
this.setState({
- isPending: r.queue.some((task: any) => task.status === STATUSES.PENDING),
- isInProgress: r.queue.some((task: any) => task.status === STATUSES.IN_PROGRESS),
- isFailed: r.current && r.current.status === STATUSES.FAILED
+ currentTask: r.current,
+ isInProgress: r.queue.some(task => task.status === STATUSES.IN_PROGRESS),
+ isPending: r.queue.some(task => task.status === STATUSES.PENDING)
});
}
});
};
render() {
+ const { currentTask } = this.state;
+ const showNotif = currentTask && currentTask.status === STATUSES.FAILED;
return (
- <ContextNavBar id="context-navigation" height={65}>
+ <ContextNavBar
+ id="context-navigation"
+ height={showNotif ? 95 : 65}
+ notif={
+ showNotif ? <ComponentNavBgTaskNotif component={this.props.component} /> : undefined
+ }>
<ComponentNavFavorite
component={this.props.component.key}
favorite={this.props.component.isFavorite}
/>
-
<ComponentNavBreadcrumbs
component={this.props.component}
breadcrumbs={this.props.component.breadcrumbs}
/>
-
{this.props.currentBranch && (
<ComponentNavBranch
branches={this.props.branches}
location={this.props.location}
/>
)}
-
<ComponentNavMeta
branch={this.props.currentBranch}
component={this.props.component}
isInProgress={this.state.isInProgress}
- isFailed={this.state.isFailed}
isPending={this.state.isPending}
/>
-
<ComponentNavMenu
branch={this.props.currentBranch}
component={this.props.component}
--- /dev/null
+/*
+ * 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.
+ */
+import * as React from 'react';
+import NavBarNotif from '../../../../components/nav/NavBarNotif';
+import { Component } from '../../../types';
+import { getComponentBackgroundTaskUrl } from '../../../../helpers/urls';
+import { translate, translateWithParameters } from '../../../../helpers/l10n';
+
+interface Props {
+ component: Component;
+}
+
+export default function ComponentNavBgTaskNotif({ component }: Props) {
+ const canSeeBackgroundTasks =
+ component.configuration != undefined && component.configuration.showBackgroundTasks;
+
+ const message = canSeeBackgroundTasks
+ ? translateWithParameters(
+ 'component_navigation.status.failed.admin',
+ getComponentBackgroundTaskUrl(component.key)
+ )
+ : translate('component_navigation.status.failed');
+
+ return (
+ <NavBarNotif className="alert alert-danger">
+ <span dangerouslySetInnerHTML={{ __html: message }} />
+ </NavBarNotif>
+ );
+}
branch?: Branch;
component: Component;
isInProgress?: boolean;
- isFailed?: boolean;
isPending?: boolean;
}
</li>
</Tooltip>
);
- } else if (props.isFailed) {
- const tooltip = canSeeBackgroundTasks
- ? translateWithParameters('component_navigation.status.failed.admin', backgroundTasksUrl)
- : translate('component_navigation.status.failed');
- metaList.push(
- <Tooltip
- key="isFailed"
- overlay={<div dangerouslySetInnerHTML={{ __html: tooltip }} />}
- mouseLeaveDelay={2}>
- <li>
- <span className="badge badge-danger">{translate('background_task.status.FAILED')}</span>
- </li>
- </Tooltip>
- );
}
if (props.component.analysisDate) {
--- /dev/null
+/*
+ * 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.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ComponentNavBgTaskNotif from '../ComponentNavBgTaskNotif';
+
+const component = {
+ analysisDate: '2017-01-02T00:00:00.000Z',
+ breadcrumbs: [],
+ key: 'foo',
+ name: 'Foo',
+ organization: 'org',
+ qualifier: 'TRK',
+ version: '0.0.1'
+};
+
+it('renders background task notif correctly', () => {
+ expect(shallow(<ComponentNavBgTaskNotif component={component} />)).toMatchSnapshot();
+});
"qualifier": "TRK",
}
}
- isFailed={true}
isInProgress={true}
isPending={true}
/>
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders background task notif correctly 1`] = `
+<NavBarNotif
+ className="alert alert-danger"
+>
+ <span
+ dangerouslySetInnerHTML={
+ Object {
+ "__html": "component_navigation.status.failed",
+ }
+ }
+ />
+</NavBarNotif>
+`;
border-bottom: 1px solid #e6e6e6;
}
+.navbar-context .navbar-inner-with-notif {
+ border-bottom: none;
+}
+
.navbar-context-header {
float: left;
line-height: 30px;
padding-left: 20px;
padding-right: 20px;
}
+
+.navbar-notif.alert {
+ border-left: none;
+ border-right: none;
+ padding: 6px 0;
+}
*/
import * as React from 'react';
import * as classNames from 'classnames';
+import NavBarNotif from './NavBarNotif';
import './NavBar.css';
interface Props {
children?: any;
className?: string;
height: number;
+ notif?: React.ReactElement<NavBarNotif>;
}
-export default function NavBar({ children, className, height, ...other }: Props) {
+export default function NavBar({ children, className, height, notif, ...other }: Props) {
return (
<nav {...other} className={classNames('navbar', className)} style={{ height }}>
- <div className="navbar-inner" style={{ height }}>
+ <div
+ className={classNames('navbar-inner', { 'navbar-inner-with-notif': notif != null })}
+ style={{ height }}>
<div className="navbar-limited clearfix">{children}</div>
+ {notif}
</div>
</nav>
);
--- /dev/null
+/*
+ * 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.
+ */
+import * as React from 'react';
+import * as classNames from 'classnames';
+
+interface Props {
+ children?: React.ReactNode;
+ className?: string;
+}
+
+export default class NavBarNotif extends React.PureComponent<Props> {
+ render() {
+ if (!this.props.children) {
+ return null;
+ }
+ return (
+ <div className={classNames('navbar-notif', this.props.className)}>
+ <div className="navbar-limited clearfix">{this.props.children}</div>
+ </div>
+ );
+ }
+}
return getBaseUrl() + '/dashboard?id=' + encodeURIComponent(componentKey) + branchQuery;
}
+export function getComponentBackgroundTaskUrl(componentKey: string): string {
+ return getBaseUrl() + '/project/background_tasks?id=' + encodeURIComponent(componentKey);
+}
+
export function getProjectUrl(key: string, branch?: string): Location {
return { pathname: '/dashboard', query: { id: key, branch } };
}
#
#------------------------------------------------------------------------------
component_navigation.status.failed=The last analysis has failed.
-component_navigation.status.failed.admin=The last analysis has failed.<br>More details available on the <a href="{0}">Background Tasks</a> page.
+component_navigation.status.failed.admin=The last analysis has failed. More details available on the <a href="{0}">Background Tasks</a> page.
component_navigation.status.pending=There is a pending analysis.
component_navigation.status.pending.admin=There is a pending analysis.<br>More details available on the <a href="{0}">Background Tasks</a> page.
component_navigation.status.in_progress=The analysis is in progress.