aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2017-11-28 14:00:11 +0100
committerStas Vilchik <stas.vilchik@sonarsource.com>2017-12-11 18:00:33 +0100
commitcd9f8d636f2b057b4c7fb6f0846e5843d44b7956 (patch)
treec9a766c979a0bbeba63e16ed7744bcac3c7583c7 /server
parentb5825706ab56c92dfae7e12d91af4891e363e03b (diff)
downloadsonarqube-cd9f8d636f2b057b4c7fb6f0846e5843d44b7956.tar.gz
sonarqube-cd9f8d636f2b057b4c7fb6f0846e5843d44b7956.zip
SONAR-10080 turn Issues to My Issues
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/api/issues.ts3
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js15
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.js7
-rw-r--r--server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.d.ts (renamed from server/sonar-web/src/main/js/apps/issues/routes.ts)29
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.js42
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx (renamed from server/sonar-web/src/main/js/apps/issues/components/AppContainer.js)54
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/routes.js4
-rw-r--r--server/sonar-web/src/main/js/helpers/issues.ts2
9 files changed, 145 insertions, 55 deletions
diff --git a/server/sonar-web/src/main/js/api/issues.ts b/server/sonar-web/src/main/js/api/issues.ts
index 730292aead3..516fe67ce48 100644
--- a/server/sonar-web/src/main/js/api/issues.ts
+++ b/server/sonar-web/src/main/js/api/issues.ts
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { getJSON, post, postJSON, RequestData } from '../helpers/request';
+import { RawIssue } from '../helpers/issues';
export interface IssueResponse {
components?: Array<{}>;
@@ -30,7 +31,7 @@ interface IssuesResponse {
components?: Array<{}>;
debtTotal?: number;
facets: Array<{}>;
- issues: Array<{}>;
+ issues: RawIssue[];
paging: {
pageIndex: number;
pageSize: number;
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
index 02b3ab3ce11..27702bf87f7 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
@@ -64,11 +64,24 @@ export default class GlobalNavMenu extends React.PureComponent {
}
renderIssuesLink() {
+ const active = this.props.location.pathname === 'issues';
+
+ if (this.props.sonarCloud) {
+ return (
+ <li>
+ <Link
+ to={{ pathname: '/issues', query: { resolved: 'false' } }}
+ className={active ? 'active' : undefined}>
+ {translate('my_issues')}
+ </Link>
+ </li>
+ );
+ }
+
const query =
this.props.currentUser.isLoggedIn && isMySet()
? { resolved: 'false', myIssues: 'true' }
: { resolved: 'false' };
- const active = this.props.location.pathname === 'issues';
return (
<li>
<Link to={{ pathname: '/issues', query }} className={active ? 'active' : undefined}>
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js
index 0350b525fd4..3488208f508 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.js
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js
@@ -47,7 +47,8 @@ import componentRoutes from '../../apps/component/routes';
import componentMeasuresRoutes from '../../apps/component-measures/routes';
import customMeasuresRoutes from '../../apps/custom-measures/routes';
import groupsRoutes from '../../apps/groups/routes';
-import issuesRoutes from '../../apps/issues/routes';
+import Issues from '../../apps/issues/components/AppContainer';
+import IssuesPageSelector from '../../apps/issues/IssuesPageSelector';
import marketplaceRoutes from '../../apps/marketplace/routes';
import metricsRoutes from '../../apps/metrics/routes';
import overviewRoutes from '../../apps/overview/routes';
@@ -167,7 +168,7 @@ const startReactApp = () => {
path="extension/:pluginKey/:extensionKey"
component={GlobalPageExtension}
/>
- <Route path="issues" childRoutes={issuesRoutes} />
+ <Route path="issues" component={IssuesPageSelector} />
<Route path="organizations" childRoutes={organizationsRoutes} />
<Route path="projects" childRoutes={projectsRoutes} />
<Route path="quality_gates" childRoutes={qualityGatesRoutes} />
@@ -187,7 +188,7 @@ const startReactApp = () => {
path="project/extension/:pluginKey/:extensionKey"
component={ProjectPageExtension}
/>
- <Route path="project/issues" childRoutes={issuesRoutes} />
+ <Route path="project/issues" component={Issues} />
<Route path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
<Route
path="project/quality_profiles"
diff --git a/server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx b/server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx
new file mode 100644
index 00000000000..4ff236be004
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 { connect } from 'react-redux';
+import AppContainer from './components/AppContainer';
+import { CurrentUser, isLoggedIn } from '../../app/types';
+import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
+
+interface StateProps {
+ currentUser: CurrentUser;
+ onSonarCloud: boolean;
+}
+
+function IssuesPage({ currentUser, onSonarCloud, ...props }: StateProps) {
+ const myIssues = (isLoggedIn(currentUser) && onSonarCloud) || undefined;
+ return <AppContainer myIssues={myIssues} {...props} />;
+}
+
+const stateToProps = (state: any) => {
+ const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+ return {
+ currentUser: getCurrentUser(state),
+ onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true')
+ };
+};
+
+export default connect<StateProps>(stateToProps)(IssuesPage);
diff --git a/server/sonar-web/src/main/js/apps/issues/routes.ts b/server/sonar-web/src/main/js/apps/issues/components/App.d.ts
index e915d90da0a..d80df8ebb32 100644
--- a/server/sonar-web/src/main/js/apps/issues/routes.ts
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.d.ts
@@ -1,7 +1,7 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
+ * mailto:contact 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
@@ -17,17 +17,20 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { RouterState, IndexRouteProps } from 'react-router';
-import { onEnter } from './redirects';
+import * as React from 'react';
+import { Component, CurrentUser } from '../../../app/types';
+import { RawQuery } from '../../../helpers/query';
-const routes = [
- {
- getIndexRoute(_: RouterState, callback: (err: any, route: IndexRouteProps) => any) {
- import('./components/AppContainer').then(i =>
- callback(null, { component: i.default, onEnter })
- );
- }
- }
-];
+interface Props {
+ branch?: { name: string };
+ component?: Component;
+ currentUser: CurrentUser;
+ fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<any>;
+ location: { pathname: string; query: RawQuery };
+ myIssues?: boolean;
+ onBranchesChange: () => void;
+ onSonarCloud: boolean;
+ organization?: { key: string };
+}
-export default routes;
+export default class App extends React.Component<Props> {}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js
index 94b226cb89d..46cb1d23952 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/App.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.js
@@ -22,6 +22,7 @@ import React from 'react';
import Helmet from 'react-helmet';
import key from 'keymaster';
import { keyBy, without } from 'lodash';
+import PropTypes from 'prop-types';
import PageActions from './PageActions';
import FiltersHeader from './FiltersHeader';
import MyIssuesFilter from './MyIssuesFilter';
@@ -71,12 +72,10 @@ export type Props = {
currentUser: CurrentUser,
fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<*>,
location: { pathname: string, query: RawQuery },
+ myIssues?: bool;
onBranchesChange: () => void,
+ onSonarCloud: bool,
organization?: { key: string },
- router: {
- push: ({ pathname: string, query?: RawQuery }) => void,
- replace: ({ pathname: string, query?: RawQuery }) => void
- }
};
*/
@@ -114,6 +113,10 @@ export default class App extends React.PureComponent {
/*:: props: Props; */
/*:: state: State; */
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
+
constructor(props /*: Props */) {
super(props);
this.state = {
@@ -123,7 +126,7 @@ export default class App extends React.PureComponent {
issues: [],
loading: true,
locationsNavigator: false,
- myIssues: areMyIssuesSelected(props.location.query),
+ myIssues: props.myIssues || areMyIssuesSelected(props.location.query),
openFacets: { severities: true, types: true },
openIssue: null,
openPopup: null,
@@ -172,7 +175,7 @@ export default class App extends React.PureComponent {
}
this.setState({
- myIssues: areMyIssuesSelected(nextProps.location.query),
+ myIssues: nextProps.myIssues || areMyIssuesSelected(nextProps.location.query),
openIssue,
query: parseQuery(nextProps.location.query)
});
@@ -329,15 +332,15 @@ export default class App extends React.PureComponent {
}
};
if (this.state.openIssue) {
- this.props.router.replace(path);
+ this.context.router.replace(path);
} else {
- this.props.router.push(path);
+ this.context.router.push(path);
}
};
closeIssue = () => {
if (this.state.query) {
- this.props.router.push({
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...serializeQuery(this.state.query),
@@ -575,7 +578,7 @@ export default class App extends React.PureComponent {
};
handleFilterChange = (changes /*: {} */) => {
- this.props.router.push({
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...serializeQuery({ ...this.state.query, ...changes }),
@@ -591,7 +594,7 @@ export default class App extends React.PureComponent {
if (!this.props.component) {
saveMyIssues(myIssues);
}
- this.props.router.push({
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...serializeQuery({ ...this.state.query, assigned: true, assignees: [] }),
@@ -618,7 +621,7 @@ export default class App extends React.PureComponent {
};
handleReset = () => {
- this.props.router.push({
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...DEFAULT_QUERY,
@@ -754,17 +757,18 @@ export default class App extends React.PureComponent {
}
renderFacets() {
- const { component, currentUser } = this.props;
+ const { component, currentUser, onSonarCloud } = this.props;
const { query } = this.state;
return (
<div className="layout-page-filters">
- {currentUser.isLoggedIn && (
- <MyIssuesFilter
- myIssues={this.state.myIssues}
- onMyIssuesChange={this.handleMyIssuesChange}
- />
- )}
+ {currentUser.isLoggedIn &&
+ !onSonarCloud && (
+ <MyIssuesFilter
+ myIssues={this.state.myIssues}
+ onMyIssuesChange={this.handleMyIssuesChange}
+ />
+ )}
<FiltersHeader displayReset={this.isFiltered()} onReset={this.handleReset} />
<Sidebar
component={component}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx
index d3af96718f6..7747e8aa148 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx
@@ -17,25 +17,37 @@
* 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 { connect } from 'react-redux';
-import { withRouter } from 'react-router';
-/*:: import type { Dispatch } from 'redux'; */
+import { Dispatch } from 'redux';
import { uniq } from 'lodash';
-import App from './App';
import throwGlobalError from '../../../app/utils/throwGlobalError';
-import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer';
+import {
+ getCurrentUser,
+ areThereCustomOrganizations,
+ getGlobalSettingValue
+} from '../../../store/rootReducer';
import { getOrganizations } from '../../../api/organizations';
import { receiveOrganizations } from '../../../store/organizations/duck';
import { searchIssues } from '../../../api/issues';
import { parseIssueFromResponse } from '../../../helpers/issues';
-/*:: import type { RawQuery } from '../../../helpers/query'; */
+import { RawQuery } from '../../../helpers/query';
+import { CurrentUser } from '../../../app/types';
+import { lazyLoad } from '../../../components/lazyLoad';
-const mapStateToProps = state => ({
- currentUser: getCurrentUser(state)
-});
+interface StateProps {
+ currentUser: CurrentUser;
+ onSonarCloud: boolean;
+}
-const fetchIssueOrganizations = issues => dispatch => {
+const mapStateToProps = (state: any): StateProps => {
+ const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+ return {
+ currentUser: getCurrentUser(state),
+ onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true')
+ };
+};
+
+const fetchIssueOrganizations = (issues: any[]) => (dispatch: Dispatch<any>) => {
if (!issues.length) {
return Promise.resolve();
}
@@ -47,9 +59,10 @@ const fetchIssueOrganizations = issues => dispatch => {
);
};
-const fetchIssues = (query /*: RawQuery */, requestOrganizations /*: boolean */ = true) => (
- dispatch,
- getState
+const fetchIssues = (query: RawQuery, requestOrganizations = true) => (
+ // use `Function` to be able to do `dispatch(...).then(...)`
+ dispatch: Function,
+ getState: () => any
) => {
const organizationsEnabled = areThereCustomOrganizations(getState());
return searchIssues({ ...query, additionalFields: '_all' })
@@ -67,6 +80,17 @@ const fetchIssues = (query /*: RawQuery */, requestOrganizations /*: boolean */
.catch(throwGlobalError);
};
-const mapDispatchToProps = { fetchIssues };
+interface DispatchProps {
+ fetchIssues: (query: RawQuery, requestOrganizations?: boolean) => Promise<void>;
+}
+
+// have to type cast this, because of async action
+const mapDispatchToProps = { fetchIssues: fetchIssues as any } as DispatchProps;
+
+interface OwnProps {
+ myIssues?: boolean;
+}
-export default connect(mapStateToProps, mapDispatchToProps)(withRouter(App));
+export default connect<StateProps, DispatchProps, OwnProps>(mapStateToProps, mapDispatchToProps)(
+ lazyLoad(() => import('./App'))
+);
diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.js b/server/sonar-web/src/main/js/apps/organizations/routes.js
index b0010c5e416..d4cbdaa8a9c 100644
--- a/server/sonar-web/src/main/js/apps/organizations/routes.js
+++ b/server/sonar-web/src/main/js/apps/organizations/routes.js
@@ -32,7 +32,7 @@ import OrganizationProjectsManagement from './components/OrganizationProjectsMan
import OrganizationDelete from './components/OrganizationDelete';
import qualityGatesRoutes from '../quality-gates/routes';
import qualityProfilesRoutes from '../quality-profiles/routes';
-import issuesRoutes from '../issues/routes';
+import Issues from '../issues/components/AppContainer';
const routes = [
{
@@ -55,7 +55,7 @@ const routes = [
{
path: 'issues',
component: OrganizationContainer,
- childRoutes: issuesRoutes
+ childRoutes: [{ indexRoute: { component: Issues } }]
},
{
path: 'members',
diff --git a/server/sonar-web/src/main/js/helpers/issues.ts b/server/sonar-web/src/main/js/helpers/issues.ts
index 33f8792bab6..c3b6b644c08 100644
--- a/server/sonar-web/src/main/js/helpers/issues.ts
+++ b/server/sonar-web/src/main/js/helpers/issues.ts
@@ -50,7 +50,7 @@ interface IssueBase {
[x: string]: any;
}
-interface RawIssue extends IssueBase {
+export interface RawIssue extends IssueBase {
assignee?: string;
author?: string;
comments?: Array<Comment>;