aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-06-13 11:15:52 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-07-04 14:15:34 +0200
commit47b553e761f7e061cdce150003123f1f5de724be (patch)
treedf209129f10f5f448acaf32ffbc119e0f80050f3
parentab94fb19012b9686efeef543909d2b555fcf8c42 (diff)
downloadsonarqube-47b553e761f7e061cdce150003123f1f5de724be.tar.gz
sonarqube-47b553e761f7e061cdce150003123f1f5de724be.zip
SONAR-9401 Remove usage of redux on the project activity page
* Create a query helper library and use it in issues page and project activity page
-rw-r--r--server/sonar-web/src/main/js/api/projectActivity.js25
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistory.js3
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.js10
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/AppContainer.js5
-rw-r--r--server/sonar-web/src/main/js/apps/issues/redirects.js2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/utils.js118
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js52
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/Analysis.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/actions-test.js.snap85
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js106
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/actions.js119
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/Event.js40
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/Events.js14
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js37
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js29
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js173
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js (renamed from server/sonar-web/src/main/js/apps/overview/actions.js)19
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageFooter.js45
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js14
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddCustomEventForm.js40
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddVersionForm.js40
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeCustomEventForm.js41
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js3
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js18
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveCustomEventForm.js48
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveVersionForm.js48
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/routes.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/types.js (renamed from server/sonar-web/src/main/js/store/projectActivity/paging.js)27
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/utils.js (renamed from server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeVersionForm.js)35
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.js5
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PageHeader.js3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/store/utils.js2
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/__snapshots__/query-test.js.snap9
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/query-test.js84
-rw-r--r--server/sonar-web/src/main/js/helpers/query.js68
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analyses-test.js.snap95
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analysesByProject-test.js.snap57
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/duck-test.js.snap77
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/events-test.js.snap73
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/paging-test.js.snap23
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/analyses-test.js90
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/analysesByProject-test.js80
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/duck-test.js99
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/events-test.js90
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/__tests__/paging-test.js49
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/analyses.js87
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/analysesByProject.js53
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/duck.js144
-rw-r--r--server/sonar-web/src/main/js/store/projectActivity/events.js77
-rw-r--r--server/sonar-web/src/main/js/store/rootReducer.js4
56 files changed, 770 insertions, 1718 deletions
diff --git a/server/sonar-web/src/main/js/api/projectActivity.js b/server/sonar-web/src/main/js/api/projectActivity.js
index 131256b0696..8ffe600e8df 100644
--- a/server/sonar-web/src/main/js/api/projectActivity.js
+++ b/server/sonar-web/src/main/js/api/projectActivity.js
@@ -30,30 +30,15 @@ type GetProjectActivityResponse = {
};
type GetProjectActivityOptions = {
+ project: string,
category?: ?string,
- pageIndex?: ?number,
- pageSize?: ?number
+ p?: ?number,
+ ps?: ?number
};
export const getProjectActivity = (
- project: string,
- options?: GetProjectActivityOptions
-): Promise<GetProjectActivityResponse> => {
- const data: Object = { project };
- if (options) {
- if (options.category) {
- data.category = options.category;
- }
- if (options.pageIndex) {
- data.p = options.pageIndex;
- }
- if (options.pageSize) {
- data.ps = options.pageSize;
- }
- }
-
- return getJSON('/api/project_analyses/search', data);
-};
+ data: GetProjectActivityOptions
+): Promise<GetProjectActivityResponse> => getJSON('/api/project_analyses/search', data);
type CreateEventResponse = {
analysis: string,
diff --git a/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistory.js b/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistory.js
index af8e4bde73c..c653d64e7b8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistory.js
+++ b/server/sonar-web/src/main/js/apps/component-measures/details/history/MeasureHistory.js
@@ -88,7 +88,8 @@ export default class MeasureHistory extends React.PureComponent {
return Promise.resolve([]);
}
- return getProjectActivity(this.props.component.key, {
+ return getProjectActivity({
+ project: this.props.component.key,
category: 'VERSION'
}).then(({ analyses }) => {
const events = analyses.map(analysis => {
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 d827a0d1f1f..e003d9ec0ea 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
@@ -58,15 +58,19 @@ import EmptySearch from '../../../components/common/EmptySearch';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { scrollToElement } from '../../../helpers/scrolling';
import type { Issue } from '../../../components/issue/types';
+import type { RawQuery } from '../../../helpers/query';
import '../styles.css';
export type Props = {
component?: Component,
currentUser: CurrentUser,
- fetchIssues: ({}) => Promise<*>,
- location: { pathname: string, query: { [string]: string } },
+ fetchIssues: (query: RawQuery) => Promise<*>,
+ location: { pathname: string, query: RawQuery },
onRequestFail: Error => void,
- router: { push: ({}) => void, replace: ({}) => void }
+ router: {
+ push: ({ pathname: string, query?: RawQuery }) => void,
+ replace: ({ pathname: string, query?: RawQuery }) => void
+ }
};
export type State = {
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.js
index 3b2ce078438..0ce4f8bb598 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js
@@ -29,8 +29,7 @@ import { getOrganizations } from '../../../api/organizations';
import { receiveOrganizations } from '../../../store/organizations/duck';
import { searchIssues } from '../../../api/issues';
import { parseIssueFromResponse } from '../../../helpers/issues';
-
-type Query = { [string]: string };
+import type { RawQuery } from '../../../helpers/query';
const mapStateToProps = (state, ownProps) => ({
component: ownProps.location.query.id
@@ -51,7 +50,7 @@ const fetchIssueOrganizations = issues => dispatch => {
);
};
-const fetchIssues = (query: Query) => dispatch =>
+const fetchIssues = (query: RawQuery) => dispatch =>
searchIssues({ ...query, additionalFields: '_all' })
.then(response => {
const parsedIssues = response.issues.map(issue =>
diff --git a/server/sonar-web/src/main/js/apps/issues/redirects.js b/server/sonar-web/src/main/js/apps/issues/redirects.js
index 3efc1af64be..a66bb570a7b 100644
--- a/server/sonar-web/src/main/js/apps/issues/redirects.js
+++ b/server/sonar-web/src/main/js/apps/issues/redirects.js
@@ -19,7 +19,7 @@
*/
// @flow
import { parseQuery, areMyIssuesSelected, serializeQuery } from './utils';
-import type { RawQuery } from './utils';
+import type { RawQuery } from '../../helpers/query';
const parseHash = (hash: string): RawQuery => {
const query: RawQuery = {};
diff --git a/server/sonar-web/src/main/js/apps/issues/utils.js b/server/sonar-web/src/main/js/apps/issues/utils.js
index d33defd8135..72451971f7a 100644
--- a/server/sonar-web/src/main/js/apps/issues/utils.js
+++ b/server/sonar-web/src/main/js/apps/issues/utils.js
@@ -18,11 +18,19 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
-import { isNil, omitBy } from 'lodash';
import { searchMembers } from '../../api/organizations';
import { searchUsers } from '../../api/users';
-
-export type RawQuery = { [string]: string };
+import {
+ queriesEqual,
+ cleanQuery,
+ parseAsBoolean,
+ parseAsFacetMode,
+ parseAsArray,
+ parseAsString,
+ serializeString,
+ serializeStringArray
+} from '../../helpers/query';
+import type { RawQuery } from '../../helpers/query';
export type Query = {|
assigned: boolean,
@@ -56,112 +64,70 @@ export type Paging = {
total: number
};
-const parseAsBoolean = (value: ?string, defaultValue: boolean = true): boolean =>
- (value === 'false' ? false : value === 'true' ? true : defaultValue);
-
-const parseAsString = (value: ?string): string => value || '';
-
-const parseAsStringArray = (value: ?string): Array<string> => (value ? value.split(',') : []);
-
-const parseAsFacetMode = (facetMode: string) =>
- (facetMode === 'debt' || facetMode === 'effort' ? 'effort' : 'count');
-
// allow sorting by CREATION_DATE only
const parseAsSort = (sort: string): string => (sort === 'CREATION_DATE' ? 'CREATION_DATE' : '');
export const parseQuery = (query: RawQuery): Query => ({
assigned: parseAsBoolean(query.assigned),
- assignees: parseAsStringArray(query.assignees),
- authors: parseAsStringArray(query.authors),
+ assignees: parseAsArray(query.assignees, parseAsString),
+ authors: parseAsArray(query.authors, parseAsString),
createdAfter: parseAsString(query.createdAfter),
createdAt: parseAsString(query.createdAt),
createdBefore: parseAsString(query.createdBefore),
createdInLast: parseAsString(query.createdInLast),
- directories: parseAsStringArray(query.directories),
+ directories: parseAsArray(query.directories, parseAsString),
facetMode: parseAsFacetMode(query.facetMode),
- files: parseAsStringArray(query.fileUuids),
- issues: parseAsStringArray(query.issues),
- languages: parseAsStringArray(query.languages),
- modules: parseAsStringArray(query.moduleUuids),
- projects: parseAsStringArray(query.projectUuids),
+ files: parseAsArray(query.fileUuids, parseAsString),
+ issues: parseAsArray(query.issues, parseAsString),
+ languages: parseAsArray(query.languages, parseAsString),
+ modules: parseAsArray(query.moduleUuids, parseAsString),
+ projects: parseAsArray(query.projectUuids, parseAsString),
resolved: parseAsBoolean(query.resolved),
- resolutions: parseAsStringArray(query.resolutions),
- rules: parseAsStringArray(query.rules),
+ resolutions: parseAsArray(query.resolutions, parseAsString),
+ rules: parseAsArray(query.rules, parseAsString),
sort: parseAsSort(query.s),
- severities: parseAsStringArray(query.severities),
+ severities: parseAsArray(query.severities, parseAsString),
sinceLeakPeriod: parseAsBoolean(query.sinceLeakPeriod, false),
- statuses: parseAsStringArray(query.statuses),
- tags: parseAsStringArray(query.tags),
- types: parseAsStringArray(query.types)
+ statuses: parseAsArray(query.statuses, parseAsString),
+ tags: parseAsArray(query.tags, parseAsString),
+ types: parseAsArray(query.types, parseAsString)
});
export const getOpen = (query: RawQuery) => query.open;
export const areMyIssuesSelected = (query: RawQuery): boolean => query.myIssues === 'true';
-const serializeString = (value: string): ?string => value || undefined;
-
-const serializeValue = (value: Array<string>): ?string => (value.length ? value.join() : undefined);
-
export const serializeQuery = (query: Query): RawQuery => {
const filter = {
assigned: query.assigned ? undefined : 'false',
- assignees: serializeValue(query.assignees),
- authors: serializeValue(query.authors),
+ assignees: serializeStringArray(query.assignees),
+ authors: serializeStringArray(query.authors),
createdAfter: serializeString(query.createdAfter),
createdAt: serializeString(query.createdAt),
createdBefore: serializeString(query.createdBefore),
createdInLast: serializeString(query.createdInLast),
- directories: serializeValue(query.directories),
+ directories: serializeStringArray(query.directories),
facetMode: query.facetMode === 'effort' ? serializeString(query.facetMode) : undefined,
- fileUuids: serializeValue(query.files),
- issues: serializeValue(query.issues),
- languages: serializeValue(query.languages),
- moduleUuids: serializeValue(query.modules),
- projectUuids: serializeValue(query.projects),
+ fileUuids: serializeStringArray(query.files),
+ issues: serializeStringArray(query.issues),
+ languages: serializeStringArray(query.languages),
+ moduleUuids: serializeStringArray(query.modules),
+ projectUuids: serializeStringArray(query.projects),
resolved: query.resolved ? undefined : 'false',
- resolutions: serializeValue(query.resolutions),
+ resolutions: serializeStringArray(query.resolutions),
s: serializeString(query.sort),
- severities: serializeValue(query.severities),
+ severities: serializeStringArray(query.severities),
sinceLeakPeriod: query.sinceLeakPeriod ? 'true' : undefined,
- statuses: serializeValue(query.statuses),
- rules: serializeValue(query.rules),
- tags: serializeValue(query.tags),
- types: serializeValue(query.types)
+ statuses: serializeStringArray(query.statuses),
+ rules: serializeStringArray(query.rules),
+ tags: serializeStringArray(query.tags),
+ types: serializeStringArray(query.types)
};
- return omitBy(filter, isNil);
-};
-
-const areArraysEqual = (a: Array<string>, b: Array<string>) => {
- if (a.length !== b.length) {
- return false;
- }
- for (let i = 0; i < a.length; i++) {
- if (a[i] !== b[i]) {
- return false;
- }
- }
- return true;
+ return cleanQuery(filter);
};
-export const areQueriesEqual = (a: RawQuery, b: RawQuery) => {
- const parsedA: Query = parseQuery(a);
- const parsedB: Query = parseQuery(b);
-
- const keysA = Object.keys(parsedA);
- const keysB = Object.keys(parsedB);
-
- if (keysA.length !== keysB.length) {
- return false;
- }
-
- return keysA.every(
- key =>
- (Array.isArray(parsedA[key]) && Array.isArray(parsedB[key])
- ? areArraysEqual(parsedA[key], parsedB[key])
- : parsedA[key] === parsedB[key])
- );
-};
+export const areQueriesEqual = (a: RawQuery, b: RawQuery) =>
+ queriesEqual(parseQuery(a), parseQuery(b));
type RawFacet = {
property: string,
diff --git a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js
index cd4ef7c68e4..6500ae188be 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js
+++ b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js
@@ -20,26 +20,27 @@
// @flow
import React from 'react';
import { Link } from 'react-router';
-import { connect } from 'react-redux';
import Analysis from './Analysis';
+import throwGlobalError from '../../../app/utils/throwGlobalError';
+import { getProjectActivity } from '../../../api/projectActivity';
import { translate } from '../../../helpers/l10n';
-import { fetchRecentProjectActivity } from '../actions';
-import { getProjectActivity } from '../../../store/rootReducer';
-import { getAnalyses } from '../../../store/projectActivity/duck';
+import type { Analysis as AnalysisType } from '../../projectActivity/types';
type Props = {
- analyses?: Array<*>,
- project: string,
- fetchRecentProjectActivity: (project: string) => Promise<*>
+ project: string
};
-class AnalysesList extends React.PureComponent {
+type State = {
+ analyses: Array<AnalysisType>,
+ loading: boolean
+};
+
+const PAGE_SIZE = 5;
+
+export default class AnalysesList extends React.PureComponent {
mounted: boolean;
props: Props;
-
- state = {
- loading: true
- };
+ state: State = { analyses: [], loading: true };
componentDidMount() {
this.mounted = true;
@@ -58,14 +59,16 @@ class AnalysesList extends React.PureComponent {
fetchData() {
this.setState({ loading: true });
- this.props.fetchRecentProjectActivity(this.props.project).then(() => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- });
+ getProjectActivity({ project: this.props.project, ps: PAGE_SIZE })
+ .then(({ analyses }) => {
+ if (this.mounted) {
+ this.setState({ analyses, loading: false });
+ }
+ })
+ .catch(throwGlobalError);
}
- renderList(analyses) {
+ renderList(analyses: Array<AnalysisType>) {
if (!analyses.length) {
return (
<p className="spacer-top note">
@@ -82,10 +85,9 @@ class AnalysesList extends React.PureComponent {
}
render() {
- const { analyses } = this.props;
- const { loading } = this.state;
+ const { analyses, loading } = this.state;
- if (loading || !analyses) {
+ if (loading) {
return null;
}
@@ -106,11 +108,3 @@ class AnalysesList extends React.PureComponent {
);
}
}
-
-const mapStateToProps = (state, ownProps: Props) => ({
- analyses: getAnalyses(getProjectActivity(state), ownProps.project)
-});
-
-const mapDispatchToProps = { fetchRecentProjectActivity };
-
-export default connect(mapStateToProps, mapDispatchToProps)(AnalysesList);
diff --git a/server/sonar-web/src/main/js/apps/overview/events/Analysis.js b/server/sonar-web/src/main/js/apps/overview/events/Analysis.js
index 582b64b64a4..959338bb2df 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/Analysis.js
+++ b/server/sonar-web/src/main/js/apps/overview/events/Analysis.js
@@ -22,7 +22,7 @@ import Events from '../../projectActivity/components/Events';
import FormattedDate from '../../../components/ui/FormattedDate';
import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
import { translate } from '../../../helpers/l10n';
-import type { Analysis as AnalysisType } from '../../../store/projectActivity/duck';
+import type { Analysis as AnalysisType } from '../../projectActivity/types';
export default function Analysis(props: { analysis: AnalysisType }) {
const { analysis } = props;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/actions-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/actions-test.js.snap
new file mode 100644
index 00000000000..2e1d7d96c09
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/actions-test.js.snap
@@ -0,0 +1,85 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`addCustomEvent should correctly add a custom event 1`] = `
+Object {
+ "date": "2016-10-27T12:21:15+0200",
+ "events": Array [
+ Object {
+ "category": "Custom",
+ "key": "Enew",
+ "name": "Foo",
+ },
+ ],
+ "key": "A2",
+}
+`;
+
+exports[`changeEvent should correctly update an event 1`] = `
+Object {
+ "date": "2016-10-27T16:33:50+0200",
+ "events": Array [
+ Object {
+ "category": "VERSION",
+ "key": "E1",
+ "name": "changed",
+ },
+ ],
+ "key": "A1",
+}
+`;
+
+exports[`deleteAnalysis should correctly delete an analyses 1`] = `
+Array [
+ Object {
+ "date": "2016-10-27T12:21:15+0200",
+ "events": Array [],
+ "key": "A2",
+ },
+ Object {
+ "date": "2016-10-26T12:17:29+0200",
+ "events": Array [
+ Object {
+ "category": "OTHER",
+ "key": "E2",
+ "name": "foo",
+ },
+ Object {
+ "category": "OTHER",
+ "key": "E3",
+ "name": "foo",
+ },
+ ],
+ "key": "A3",
+ },
+]
+`;
+
+exports[`deleteEvent should correctly remove an event 1`] = `
+Object {
+ "date": "2016-10-27T16:33:50+0200",
+ "events": Array [],
+ "key": "A1",
+}
+`;
+
+exports[`deleteEvent should correctly remove an event 2`] = `
+Object {
+ "date": "2016-10-27T12:21:15+0200",
+ "events": Array [],
+ "key": "A2",
+}
+`;
+
+exports[`deleteEvent should correctly remove an event 3`] = `
+Object {
+ "date": "2016-10-26T12:17:29+0200",
+ "events": Array [
+ Object {
+ "category": "OTHER",
+ "key": "E3",
+ "name": "foo",
+ },
+ ],
+ "key": "A3",
+}
+`;
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js
new file mode 100644
index 00000000000..896a172c9a4
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js
@@ -0,0 +1,106 @@
+/*
+ * 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 actions from '../actions';
+
+const ANALYSES = [
+ {
+ key: 'A1',
+ date: '2016-10-27T16:33:50+0200',
+ events: [
+ {
+ key: 'E1',
+ category: 'VERSION',
+ name: '6.5-SNAPSHOT'
+ }
+ ]
+ },
+ {
+ key: 'A2',
+ date: '2016-10-27T12:21:15+0200',
+ events: []
+ },
+ {
+ key: 'A3',
+ date: '2016-10-26T12:17:29+0200',
+ events: [
+ {
+ key: 'E2',
+ category: 'OTHER',
+ name: 'foo'
+ },
+ {
+ key: 'E3',
+ category: 'OTHER',
+ name: 'foo'
+ }
+ ]
+ }
+];
+
+const newEvent = {
+ key: 'Enew',
+ name: 'Foo',
+ category: 'Custom'
+};
+
+it('should never throw when there is no analyses', () => {
+ expect(actions.addCustomEvent('A1', newEvent)({})).toBeUndefined();
+ expect(actions.deleteEvent('A1', newEvent)({})).toBeUndefined();
+ expect(actions.changeEvent('A1', newEvent)({})).toBeUndefined();
+ expect(actions.deleteAnalysis('Anew')({})).toBeUndefined();
+});
+
+describe('addCustomEvent', () => {
+ it('should correctly add a custom event', () => {
+ expect(
+ actions.addCustomEvent('A2', newEvent)({ analyses: ANALYSES }).analyses[1]
+ ).toMatchSnapshot();
+ expect(
+ actions.addCustomEvent('A1', newEvent)({ analyses: ANALYSES }).analyses[0].events
+ ).toContain(newEvent);
+ });
+});
+
+describe('deleteEvent', () => {
+ it('should correctly remove an event', () => {
+ expect(actions.deleteEvent('A1', 'E1')({ analyses: ANALYSES }).analyses[0]).toMatchSnapshot();
+ expect(actions.deleteEvent('A2', 'E1')({ analyses: ANALYSES }).analyses[1]).toMatchSnapshot();
+ expect(actions.deleteEvent('A3', 'E2')({ analyses: ANALYSES }).analyses[2]).toMatchSnapshot();
+ });
+});
+
+describe('changeEvent', () => {
+ it('should correctly update an event', () => {
+ expect(
+ actions.changeEvent('A1', { key: 'E1', name: 'changed' })({ analyses: ANALYSES }).analyses[0]
+ ).toMatchSnapshot();
+ expect(
+ actions.changeEvent('A2', { key: 'E2' })({ analyses: ANALYSES }).analyses[1].events
+ ).toHaveLength(0);
+ });
+});
+
+describe('deleteAnalysis', () => {
+ it('should correctly delete an analyses', () => {
+ expect(actions.deleteAnalysis('A1')({ analyses: ANALYSES }).analyses).toMatchSnapshot();
+ expect(actions.deleteAnalysis('A5')({ analyses: ANALYSES }).analyses).toHaveLength(3);
+ expect(actions.deleteAnalysis('A2')({ analyses: ANALYSES }).analyses).toHaveLength(2);
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/actions.js b/server/sonar-web/src/main/js/apps/projectActivity/actions.js
index 60a2dbcb480..e4e36ff0f85 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/actions.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/actions.js
@@ -18,81 +18,44 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
-import * as api from '../../api/projectActivity';
-import {
- receiveProjectActivity,
- addEvent,
- deleteEvent as deleteEventAction,
- changeEvent as changeEventAction,
- deleteAnalysis as deleteAnalysisAction,
- getPaging
-} from '../../store/projectActivity/duck';
-import { onFail } from '../../store/rootActions';
-import { getProjectActivity } from '../../store/rootReducer';
-
-const rejectOnFail = (dispatch: Function) => (error: Object) => {
- onFail(dispatch)(error);
- return Promise.reject();
-};
-
-export const fetchProjectActivity = (project: string, filter: ?string) => (
- dispatch: Function
-): void => {
- api
- .getProjectActivity(project, { category: filter })
- .then(
- ({ analyses, paging }) => dispatch(receiveProjectActivity(project, analyses, paging)),
- onFail(dispatch)
- );
-};
-
-export const fetchMoreProjectActivity = (project: string, filter: ?string) => (
- dispatch: Function,
- getState: Function
-): void => {
- const projectActivity = getProjectActivity(getState());
- const { pageIndex } = getPaging(projectActivity, project);
-
- api
- .getProjectActivity(project, { category: filter, pageIndex: pageIndex + 1 })
- .then(
- ({ analyses, paging }) => dispatch(receiveProjectActivity(project, analyses, paging)),
- onFail(dispatch)
- );
-};
-
-export const addCustomEvent = (analysis: string, name: string, category?: string) => (
- dispatch: Function
-): Promise<*> => {
- return api
- .createEvent(analysis, name, category)
- .then(({ analysis, ...event }) => dispatch(addEvent(analysis, event)), rejectOnFail(dispatch));
-};
-
-export const deleteEvent = (analysis: string, event: string) => (
- dispatch: Function
-): Promise<*> => {
- return api
- .deleteEvent(event)
- .then(() => dispatch(deleteEventAction(analysis, event)), rejectOnFail(dispatch));
-};
-
-export const addVersion = (analysis: string, version: string) => (
- dispatch: Function
-): Promise<*> => {
- return dispatch(addCustomEvent(analysis, version, 'VERSION'));
-};
-
-export const changeEvent = (event: string, name: string) => (dispatch: Function): Promise<*> => {
- return api
- .changeEvent(event, name)
- .then(() => dispatch(changeEventAction(event, { name })), rejectOnFail(dispatch));
-};
-
-export const deleteAnalysis = (project: string, analysis: string) => (
- dispatch: Function
-): Promise<*> => {
- return api
- .deleteAnalysis(analysis)
- .then(() => dispatch(deleteAnalysisAction(project, analysis)), rejectOnFail(dispatch));
-};
+import type { Event } from './types';
+import type { State } from './components/ProjectActivityApp';
+
+export const addCustomEvent = (analysis: string, event: Event) => (state: State) => ({
+ analyses: state.analyses.map(item => {
+ if (item.key !== analysis) {
+ return item;
+ }
+ return { ...item, events: [...item.events, event] };
+ })
+});
+
+export const deleteEvent = (analysis: string, event: string) => (state: State) => ({
+ analyses: state.analyses.map(item => {
+ if (item.key !== analysis) {
+ return item;
+ }
+ return {
+ ...item,
+ events: item.events.filter(eventItem => eventItem.key !== event)
+ };
+ })
+});
+
+export const changeEvent = (analysis: string, event: Event) => (state: State) => ({
+ analyses: state.analyses.map(item => {
+ if (item.key !== analysis) {
+ return item;
+ }
+ return {
+ ...item,
+ events: item.events.map(
+ eventItem => (eventItem.key === event.key ? { ...eventItem, ...event } : eventItem)
+ )
+ };
+ })
+});
+
+export const deleteAnalysis = (analysis: string) => (state: State) => ({
+ analyses: state.analyses.filter(item => item.key !== analysis)
+});
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/Event.js b/server/sonar-web/src/main/js/apps/projectActivity/components/Event.js
index 6697449dc5d..2dc04fec94c 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/Event.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/Event.js
@@ -20,17 +20,19 @@
// @flow
import React from 'react';
import EventInner from './EventInner';
-import ChangeCustomEventForm from './forms/ChangeCustomEventForm';
-import RemoveCustomEventForm from './forms/RemoveCustomEventForm';
+import ChangeEventForm from './forms/ChangeEventForm';
+import RemoveEventForm from './forms/RemoveEventForm';
import DeleteIcon from './DeleteIcon';
import ChangeIcon from './ChangeIcon';
-import type { Event as EventType } from '../../../store/projectActivity/duck';
+import type { Event as EventType } from '../types';
type Props = {
analysis: string,
+ canAdmin: boolean,
+ changeEvent: (event: string, name: string) => Promise<*>,
+ deleteEvent: (analysis: string, event: string) => Promise<*>,
event: EventType,
- isFirst: boolean,
- canAdmin: boolean
+ isFirst: boolean
};
type State = {
@@ -41,7 +43,6 @@ type State = {
export default class Event extends React.PureComponent {
mounted: boolean;
props: Props;
-
state: State = {
changing: false,
deleting: false
@@ -77,9 +78,10 @@ export default class Event extends React.PureComponent {
render() {
const { event, canAdmin } = this.props;
- const canChange = ['OTHER', 'VERSION'].includes(event.category);
- const canDelete =
- event.category === 'OTHER' || (event.category === 'VERSION' && !this.props.isFirst);
+ const isOther = event.category === 'OTHER';
+ const isVersion = !isOther && event.category === 'VERSION';
+ const canChange = isOther || isVersion;
+ const canDelete = isOther || (isVersion && !this.props.isFirst);
const showActions = canAdmin && (canChange || canDelete);
return (
@@ -99,13 +101,29 @@ export default class Event extends React.PureComponent {
</div>}
{this.state.changing &&
- <ChangeCustomEventForm event={this.props.event} onClose={this.stopChanging} />}
+ <ChangeEventForm
+ changeEventButtonText={
+ 'project_activity.' + (isVersion ? 'change_version' : 'change_custom_event')
+ }
+ changeEvent={this.props.changeEvent}
+ event={this.props.event}
+ onClose={this.stopChanging}
+ />}
{this.state.deleting &&
- <RemoveCustomEventForm
+ <RemoveEventForm
analysis={this.props.analysis}
+ deleteEvent={this.props.deleteEvent}
event={this.props.event}
onClose={this.stopDeleting}
+ removeEventButtonText={
+ 'project_activity.' + (isVersion ? 'remove_version' : 'remove_custom_event')
+ }
+ removeEventQuestion={
+ 'project_activity.' +
+ (isVersion ? 'remove_version' : 'remove_custom_event') +
+ '.question'
+ }
/>}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.js b/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.js
index 4135b322fda..62bc0e7e229 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/EventInner.js
@@ -20,7 +20,7 @@
// @flow
import React from 'react';
import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin';
-import type { Event as EventType } from '../../../store/projectActivity/duck';
+import type { Event as EventType } from '../types';
import { translate } from '../../../helpers/l10n';
import './Event.css';
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/Events.js b/server/sonar-web/src/main/js/apps/projectActivity/components/Events.js
index 36a931422a5..27678c0a9ac 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/Events.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/Events.js
@@ -21,13 +21,15 @@
import React from 'react';
import { sortBy } from 'lodash';
import Event from './Event';
-import type { Event as EventType } from '../../../store/projectActivity/duck';
+import type { Event as EventType } from '../types';
type Props = {
analysis: string,
+ canAdmin: boolean,
+ changeEvent: (event: string, name: string) => Promise<*>,
+ deleteEvent: (analysis: string, event: string) => Promise<*>,
events: Array<EventType>,
- isFirst: boolean,
- canAdmin: boolean
+ isFirst: boolean
};
export default function Events(props: Props) {
@@ -43,11 +45,13 @@ export default function Events(props: Props) {
<div className="project-activity-events">
{sortedEvents.map(event => (
<Event
- key={event.key}
analysis={props.analysis}
+ canAdmin={props.canAdmin}
+ changeEvent={props.changeEvent}
+ deleteEvent={props.deleteEvent}
event={event}
isFirst={props.isFirst}
- canAdmin={props.canAdmin}
+ key={event.key}
/>
))}
</div>
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js
index 6e95edb3b25..96ab8cb14ad 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js
@@ -19,27 +19,24 @@
*/
// @flow
import React from 'react';
-import { connect } from 'react-redux';
import { groupBy } from 'lodash';
import moment from 'moment';
import ProjectActivityAnalysis from './ProjectActivityAnalysis';
import FormattedDate from '../../../components/ui/FormattedDate';
-import { getProjectActivity } from '../../../store/rootReducer';
-import { getAnalyses } from '../../../store/projectActivity/duck';
import { translate } from '../../../helpers/l10n';
-import type { Analysis } from '../../../store/projectActivity/duck';
+import type { Analysis } from '../types';
type Props = {
- project: string,
- analyses?: Array<Analysis>,
- canAdmin: boolean
+ addCustomEvent: (analysis: string, name: string, category?: string) => Promise<*>,
+ addVersion: (analysis: string, version: string) => Promise<*>,
+ analyses: Array<Analysis>,
+ canAdmin: boolean,
+ changeEvent: (event: string, name: string) => Promise<*>,
+ deleteAnalysis: (analysis: string) => Promise<*>,
+ deleteEvent: (analysis: string, event: string) => Promise<*>
};
-function ProjectActivityAnalysesList(props: Props) {
- if (!props.analyses) {
- return null;
- }
-
+export default function ProjectActivityAnalysesList(props: Props) {
if (props.analyses.length === 0) {
return <div className="note">{translate('no_results')}</div>;
}
@@ -64,11 +61,15 @@ function ProjectActivityAnalysesList(props: Props) {
{byDay[day] != null &&
byDay[day].map(analysis => (
<ProjectActivityAnalysis
- key={analysis.key}
+ addCustomEvent={props.addCustomEvent}
+ addVersion={props.addVersion}
analysis={analysis}
- isFirst={analysis === firstAnalysis}
- project={props.project}
canAdmin={props.canAdmin}
+ changeEvent={props.changeEvent}
+ deleteAnalysis={props.deleteAnalysis}
+ deleteEvent={props.deleteEvent}
+ isFirst={analysis === firstAnalysis}
+ key={analysis.key}
/>
))}
</ul>
@@ -78,9 +79,3 @@ function ProjectActivityAnalysesList(props: Props) {
</div>
);
}
-
-const mapStateToProps = (state, ownProps) => ({
- analyses: getAnalyses(getProjectActivity(state), ownProps.project)
-});
-
-export default connect(mapStateToProps)(ProjectActivityAnalysesList);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js
index 0774d09abb7..01f933beadc 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js
@@ -20,17 +20,20 @@
// @flow
import React from 'react';
import Events from './Events';
-import AddVersionForm from './forms/AddVersionForm';
-import AddCustomEventForm from './forms/AddCustomEventForm';
+import AddEventForm from './forms/AddEventForm';
import RemoveAnalysisForm from './forms/RemoveAnalysisForm';
import FormattedDate from '../../../components/ui/FormattedDate';
-import type { Analysis } from '../../../store/projectActivity/duck';
import { translate } from '../../../helpers/l10n';
+import type { Analysis } from '../types';
type Props = {
+ addCustomEvent: (analysis: string, name: string, category?: string) => Promise<*>,
+ addVersion: (analysis: string, version: string) => Promise<*>,
analysis: Analysis,
+ changeEvent: (event: string, name: string) => Promise<*>,
+ deleteAnalysis: (analysis: string) => Promise<*>,
+ deleteEvent: (analysis: string, event: string) => Promise<*>,
isFirst: boolean,
- project: string,
canAdmin: boolean
};
@@ -51,17 +54,25 @@ export default function ProjectActivityAnalysis(props: Props) {
<ul className="dropdown-menu dropdown-menu-right">
{version == null &&
<li>
- <AddVersionForm analysis={props.analysis} />
+ <AddEventForm
+ addEvent={props.addVersion}
+ analysis={props.analysis}
+ addEventButtonText="project_activity.add_version"
+ />
</li>}
<li>
- <AddCustomEventForm analysis={props.analysis} />
+ <AddEventForm
+ addEvent={props.addCustomEvent}
+ analysis={props.analysis}
+ addEventButtonText="project_activity.add_custom_event"
+ />
</li>
</ul>
</div>
{!isFirst &&
<div className="display-inline-block little-spacer-left">
- <RemoveAnalysisForm analysis={props.analysis} project={props.project} />
+ <RemoveAnalysisForm analysis={props.analysis} deleteAnalysis={props.deleteAnalysis} />
</div>}
</div>}
@@ -72,9 +83,11 @@ export default function ProjectActivityAnalysis(props: Props) {
{events.length > 0 &&
<Events
analysis={props.analysis.key}
+ canAdmin={canAdmin}
+ changeEvent={props.changeEvent}
+ deleteEvent={props.deleteEvent}
events={events}
isFirst={props.isFirst}
- canAdmin={canAdmin}
/>}
</li>
);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
index fd9ac67fc94..7289766ece9 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
@@ -20,54 +20,151 @@
// @flow
import React from 'react';
import Helmet from 'react-helmet';
-import { connect } from 'react-redux';
import ProjectActivityPageHeader from './ProjectActivityPageHeader';
import ProjectActivityAnalysesList from './ProjectActivityAnalysesList';
import ProjectActivityPageFooter from './ProjectActivityPageFooter';
-import { fetchProjectActivity } from '../actions';
-import { getComponent } from '../../../store/rootReducer';
+import throwGlobalError from '../../../app/utils/throwGlobalError';
+import * as api from '../../../api/projectActivity';
+import * as actions from '../actions';
+import { parseQuery, serializeQuery, serializeUrlQuery } from '../utils';
import { translate } from '../../../helpers/l10n';
import './projectActivity.css';
+import type { Analysis, Query, Paging } from '../types';
+import type { RawQuery } from '../../../helpers/query';
type Props = {
- location: { query: { id: string } },
- fetchProjectActivity: (project: string, filter: ?string) => void,
- project: { configuration?: { showHistory: boolean } }
+ location: { pathname: string, query: RawQuery },
+ project: { configuration?: { showHistory: boolean }, key: string },
+ router: { push: ({ pathname: string, query?: RawQuery }) => void }
};
-type State = {
- filter: ?string
+export type State = {
+ analyses: Array<Analysis>,
+ loading: boolean,
+ paging?: Paging,
+ query: Query
};
-class ProjectActivityApp extends React.PureComponent {
+export default class ProjectActivityApp extends React.PureComponent {
+ mounted: boolean;
props: Props;
+ state: State;
- state: State = {
- filter: null
- };
+ constructor(props: Props) {
+ super(props);
+ this.state = { analyses: [], loading: true, query: parseQuery(props.location.query) };
+ }
componentDidMount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.add('dashboard-page');
+ this.mounted = true;
+ this.handleQueryChange();
+ const elem = document.querySelector('html');
+ elem && elem.classList.add('dashboard-page');
+ }
+
+ componentDidUpdate(prevProps: Props) {
+ if (prevProps.location.query !== this.props.location.query) {
+ this.handleQueryChange();
}
- this.props.fetchProjectActivity(this.props.location.query.id);
}
componentWillUnmount() {
- const html = document.querySelector('html');
- if (html) {
- html.classList.remove('dashboard-page');
+ this.mounted = false;
+ const elem = document.querySelector('html');
+ elem && elem.classList.remove('dashboard-page');
+ }
+
+ fetchActivity = (
+ query: Query,
+ additional?: {}
+ ): Promise<{ analyses: Array<Analysis>, paging: Paging }> => {
+ const parameters = {
+ ...serializeQuery(query),
+ ...additional
+ };
+ return api.getProjectActivity(parameters).catch(throwGlobalError);
+ };
+
+ fetchMoreActivity = () => {
+ const { paging, query } = this.state;
+ if (!paging) {
+ return;
}
+
+ this.setState({ loading: true });
+ this.fetchActivity(query, { p: paging.pageIndex + 1 }).then(({ analyses, paging }) => {
+ if (this.mounted) {
+ this.setState((state: State) => ({
+ analyses: state.analyses ? state.analyses.concat(analyses) : analyses,
+ loading: false,
+ paging
+ }));
+ }
+ });
+ };
+
+ addCustomEvent = (analysis: string, name: string, category?: string): Promise<*> =>
+ api
+ .createEvent(analysis, name, category)
+ .then(
+ ({ analysis, ...event }) =>
+ this.mounted && this.setState(actions.addCustomEvent(analysis, event))
+ )
+ .catch(throwGlobalError);
+
+ addVersion = (analysis: string, version: string): Promise<*> =>
+ this.addCustomEvent(analysis, version, 'VERSION');
+
+ deleteEvent = (analysis: string, event: string): Promise<*> =>
+ api
+ .deleteEvent(event)
+ .then(() => this.mounted && this.setState(actions.deleteEvent(analysis, event)))
+ .catch(throwGlobalError);
+
+ changeEvent = (event: string, name: string): Promise<*> =>
+ api
+ .changeEvent(event, name)
+ .then(
+ ({ analysis, ...event }) =>
+ this.mounted && this.setState(actions.changeEvent(analysis, event))
+ )
+ .catch(throwGlobalError);
+
+ deleteAnalysis = (analysis: string): Promise<*> =>
+ api
+ .deleteAnalysis(analysis)
+ .then(() => this.mounted && this.setState(actions.deleteAnalysis(analysis)))
+ .catch(throwGlobalError);
+
+ handleQueryChange() {
+ const query = parseQuery(this.props.location.query);
+ this.setState({ loading: true, query });
+ this.fetchActivity(query).then(({ analyses, paging }) => {
+ if (this.mounted) {
+ this.setState({
+ analyses,
+ loading: false,
+ paging
+ });
+ }
+ });
}
- handleFilter = (filter: ?string) => {
- this.setState({ filter });
- this.props.fetchProjectActivity(this.props.location.query.id, filter);
+ updateQuery = (newQuery: Query) => {
+ this.props.router.push({
+ pathname: this.props.location.pathname,
+ query: {
+ ...serializeUrlQuery({
+ ...this.state.query,
+ ...newQuery
+ }),
+ id: this.props.project.key
+ }
+ });
};
render() {
- const project = this.props.location.query.id;
+ const { query } = this.state;
const { configuration } = this.props.project;
const canAdmin = configuration ? configuration.showHistory : false;
@@ -75,24 +172,24 @@ class ProjectActivityApp extends React.PureComponent {
<div id="project-activity" className="page page-limited">
<Helmet title={translate('project_activity.page')} />
- <ProjectActivityPageHeader
- project={project}
- filter={this.state.filter}
- changeFilter={this.handleFilter}
- />
+ <ProjectActivityPageHeader category={query.category} updateQuery={this.updateQuery} />
- <ProjectActivityAnalysesList project={project} canAdmin={canAdmin} />
+ <ProjectActivityAnalysesList
+ addCustomEvent={this.addCustomEvent}
+ addVersion={this.addVersion}
+ analyses={this.state.analyses}
+ canAdmin={canAdmin}
+ changeEvent={this.changeEvent}
+ deleteAnalysis={this.deleteAnalysis}
+ deleteEvent={this.deleteEvent}
+ />
- <ProjectActivityPageFooter project={project} />
+ <ProjectActivityPageFooter
+ analyses={this.state.analyses}
+ fetchMoreActivity={this.fetchMoreActivity}
+ paging={this.state.paging}
+ />
</div>
);
}
}
-
-const mapStateToProps = (state, ownProps: Props) => ({
- project: getComponent(state, ownProps.location.query.id)
-});
-
-const mapDispatchToProps = { fetchProjectActivity };
-
-export default connect(mapStateToProps, mapDispatchToProps)(ProjectActivityApp);
diff --git a/server/sonar-web/src/main/js/apps/overview/actions.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
index 892cea5e498..9ed72083692 100644
--- a/server/sonar-web/src/main/js/apps/overview/actions.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
@@ -18,16 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
-import * as api from '../../api/projectActivity';
-import { receiveProjectActivity } from '../../store/projectActivity/duck';
-import { onFail } from '../../store/rootActions';
+import { connect } from 'react-redux';
+import { withRouter } from 'react-router';
+import ProjectActivityApp from './ProjectActivityApp';
+import { getComponent } from '../../../store/rootReducer';
-const PAGE_SIZE = 5;
+const mapStateToProps = (state, ownProps) => ({
+ project: getComponent(state, ownProps.location.query.id)
+});
-export const fetchRecentProjectActivity = (project: string) => (dispatch: Function) =>
- api
- .getProjectActivity(project, { pageSize: PAGE_SIZE })
- .then(
- ({ analyses, paging }) => dispatch(receiveProjectActivity(project, analyses, paging)),
- onFail(dispatch)
- );
+export default connect(mapStateToProps)(withRouter(ProjectActivityApp));
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageFooter.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageFooter.js
index 4bf4d21b3ac..77428e38105 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageFooter.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageFooter.js
@@ -19,43 +19,18 @@
*/
// @flow
import React from 'react';
-import { connect } from 'react-redux';
import ListFooter from '../../../components/controls/ListFooter';
-import { getProjectActivity } from '../../../store/rootReducer';
-import { getAnalyses, getPaging } from '../../../store/projectActivity/duck';
-import { fetchMoreProjectActivity } from '../actions';
-import type { Paging } from '../../../store/projectActivity/duck';
+import type { Paging } from '../types';
-class ProjectActivityPageFooter extends React.PureComponent {
- props: {
- analyses: Array<*>,
- paging: ?Paging,
- project: string,
- fetchMoreProjectActivity: (project: string) => void
- };
+type Props = {
+ analyses: Array<*>,
+ fetchMoreActivity: () => void,
+ paging?: Paging
+};
- handleLoadMore = () => {
- this.props.fetchMoreProjectActivity(this.props.project);
- };
-
- render() {
- const { analyses, paging } = this.props;
-
- if (!paging || analyses.length === 0) {
- return null;
- }
-
- return (
- <ListFooter count={analyses.length} total={paging.total} loadMore={this.handleLoadMore} />
- );
+export default function ProjectActivityPageFooter({ analyses, fetchMoreActivity, paging }: Props) {
+ if (!paging || analyses.length === 0) {
+ return null;
}
+ return <ListFooter count={analyses.length} total={paging.total} loadMore={fetchMoreActivity} />;
}
-
-const mapStateToProps = (state, ownProps) => ({
- analyses: getAnalyses(getProjectActivity(state), ownProps.project),
- paging: getPaging(getProjectActivity(state), ownProps.project)
-});
-
-const mapDispatchToProps = { fetchMoreProjectActivity };
-
-export default connect(mapStateToProps, mapDispatchToProps)(ProjectActivityPageFooter);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js
index 624559acd4c..8dd16cb5045 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityPageHeader.js
@@ -21,18 +21,20 @@
import React from 'react';
import Select from 'react-select';
import { translate } from '../../../helpers/l10n';
+import type { RawQuery } from '../../../helpers/query';
type Props = {
- changeFilter: (filter: ?string) => void,
- filter: ?string
+ updateQuery: RawQuery => void,
+ category?: string
};
export default class ProjectActivityPageHeader extends React.PureComponent {
props: Props;
- handleChange = (option: null | { value: string }) => {
- this.props.changeFilter(option && option.value);
+ handleCategoryChange = (option: ?{ value: string }) => {
+ this.props.updateQuery({ category: option ? option.value : '' });
};
+
render() {
const selectOptions = ['VERSION', 'QUALITY_GATE', 'QUALITY_PROFILE', 'OTHER'].map(category => ({
label: translate('event.category', category),
@@ -47,9 +49,9 @@ export default class ProjectActivityPageHeader extends React.PureComponent {
placeholder={translate('filter_verb') + '...'}
clearable={true}
searchable={false}
- value={this.props.filter}
+ value={this.props.category}
options={selectOptions}
- onChange={this.handleChange}
+ onChange={this.handleCategoryChange}
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddCustomEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddCustomEventForm.js
deleted file mode 100644
index 9ff07425850..00000000000
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddCustomEventForm.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 { connect } from 'react-redux';
-import { addCustomEvent } from '../../actions';
-import AddEventForm from './AddEventForm';
-import type { Analysis } from '../../../../store/projectActivity/duck';
-
-type Props = {
- addEvent: (analysis: string, name: string, category?: string) => Promise<*>,
- analysis: Analysis
-};
-
-function AddCustomEventForm(props: Props) {
- return <AddEventForm {...props} addEventButtonText="project_activity.add_custom_event" />;
-}
-
-const mapStateToProps = null;
-
-const mapDispatchToProps = { addEvent: addCustomEvent };
-
-export default connect(mapStateToProps, mapDispatchToProps)(AddCustomEventForm);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
index 759a60313df..d83b4244d6a 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
@@ -20,8 +20,8 @@
// @flow
import React from 'react';
import Modal from 'react-modal';
-import type { Analysis } from '../../../../store/projectActivity/duck';
import { translate } from '../../../../helpers/l10n';
+import type { Analysis } from '../../types';
type Props = {
addEvent: (analysis: string, name: string, category?: string) => Promise<*>,
@@ -38,7 +38,6 @@ type State = {
export default class AddEventForm extends React.PureComponent {
mounted: boolean;
props: Props;
-
state: State = {
open: false,
processing: false,
@@ -131,7 +130,6 @@ export default class AddEventForm extends React.PureComponent {
</div>}
</footer>
</form>
-
</Modal>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddVersionForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddVersionForm.js
deleted file mode 100644
index a415a6ec9e6..00000000000
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddVersionForm.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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 { connect } from 'react-redux';
-import { addVersion } from '../../actions';
-import AddEventForm from './AddEventForm';
-import type { Analysis } from '../../../../store/projectActivity/duck';
-
-type Props = {
- addEvent: (analysis: string, version: string) => Promise<*>,
- analysis: Analysis
-};
-
-function AddVersionForm(props: Props) {
- return <AddEventForm {...props} addEventButtonText="project_activity.add_version" />;
-}
-
-const mapStateToProps = null;
-
-const mapDispatchToProps = { addEvent: addVersion };
-
-export default connect(mapStateToProps, mapDispatchToProps)(AddVersionForm);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeCustomEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeCustomEventForm.js
deleted file mode 100644
index 0cbb7d0d6f5..00000000000
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeCustomEventForm.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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 { connect } from 'react-redux';
-import ChangeEventForm from './ChangeEventForm';
-import { changeEvent } from '../../actions';
-import type { Event } from '../../../../store/projectActivity/duck';
-
-type Props = {
- changeEvent: (event: string, name: string) => Promise<*>,
- event: Event,
- onClose: () => void
-};
-
-const ChangeCustomEventForm = (props: Props) => (
- <ChangeEventForm {...props} changeEventButtonText="project_activity.change_custom_event" />
-);
-
-const mapStateToProps = null;
-
-const mapDispatchToProps = { changeEvent };
-
-export default connect(mapStateToProps, mapDispatchToProps)(ChangeCustomEventForm);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
index 240ad559c06..6a091dcae76 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
@@ -20,8 +20,8 @@
// @flow
import React from 'react';
import Modal from 'react-modal';
-import type { Event } from '../../../../store/projectActivity/duck';
import { translate } from '../../../../helpers/l10n';
+import type { Event } from '../../types';
type Props = {
changeEvent: (event: string, name: string) => Promise<*>,
@@ -129,7 +129,6 @@ export default class ChangeEventForm extends React.PureComponent {
</div>}
</footer>
</form>
-
</Modal>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
index ec3676d3ed8..9b0e215c686 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
@@ -19,16 +19,13 @@
*/
// @flow
import React from 'react';
-import { connect } from 'react-redux';
import Modal from 'react-modal';
-import type { Analysis } from '../../../../store/projectActivity/duck';
import { translate } from '../../../../helpers/l10n';
-import { deleteAnalysis } from '../../actions';
+import type { Analysis } from '../../types';
type Props = {
analysis: Analysis,
- deleteAnalysis: (project: string, analysis: string) => Promise<*>,
- project: string
+ deleteAnalysis: (analysis: string) => Promise<*>
};
type State = {
@@ -36,10 +33,9 @@ type State = {
processing: boolean
};
-class RemoveAnalysisForm extends React.PureComponent {
+export default class RemoveAnalysisForm extends React.PureComponent {
mounted: boolean;
props: Props;
-
state: State = {
open: false,
processing: false
@@ -81,7 +77,7 @@ class RemoveAnalysisForm extends React.PureComponent {
e.preventDefault();
this.setState({ processing: true });
this.props
- .deleteAnalysis(this.props.project, this.props.analysis.key)
+ .deleteAnalysis(this.props.analysis.key)
.then(this.stopProcessingAndClose, this.stopProcessing);
};
@@ -128,9 +124,3 @@ class RemoveAnalysisForm extends React.PureComponent {
);
}
}
-
-const mapStateToProps = null;
-
-const mapDispatchToProps = { deleteAnalysis };
-
-export default connect(mapStateToProps, mapDispatchToProps)(RemoveAnalysisForm);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveCustomEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveCustomEventForm.js
deleted file mode 100644
index 6223957e45a..00000000000
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveCustomEventForm.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 { connect } from 'react-redux';
-import RemoveEventForm from './RemoveEventForm';
-import { deleteEvent } from '../../actions';
-import type { Event } from '../../../../store/projectActivity/duck';
-
-type Props = {
- analysis: string,
- event: Event,
- deleteEvent: (analysis: string, event: string) => Promise<*>,
- onClose: () => void
-};
-
-function RemoveCustomEventForm(props: Props) {
- return (
- <RemoveEventForm
- {...props}
- removeEventButtonText="project_activity.remove_custom_event"
- removeEventQuestion="project_activity.remove_custom_event.question"
- />
- );
-}
-
-const mapStateToProps = null;
-
-const mapDispatchToProps = { deleteEvent };
-
-export default connect(mapStateToProps, mapDispatchToProps)(RemoveCustomEventForm);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
index e17ed059e85..b23522db21c 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
@@ -20,8 +20,8 @@
// @flow
import React from 'react';
import Modal from 'react-modal';
-import type { Event } from '../../../../store/projectActivity/duck';
import { translate } from '../../../../helpers/l10n';
+import type { Event } from '../../types';
type Props = {
analysis: string,
@@ -39,7 +39,6 @@ type State = {
export default class RemoveEventForm extends React.PureComponent {
mounted: boolean;
props: Props;
-
state: State = {
processing: false
};
@@ -108,7 +107,6 @@ export default class RemoveEventForm extends React.PureComponent {
</div>}
</footer>
</form>
-
</Modal>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveVersionForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveVersionForm.js
deleted file mode 100644
index 3b0459170b8..00000000000
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveVersionForm.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 { connect } from 'react-redux';
-import RemoveEventForm from './RemoveEventForm';
-import { deleteEvent } from '../../actions';
-import type { Event } from '../../../../store/projectActivity/duck';
-
-type Props = {
- analysis: string,
- event: Event,
- deleteEvent: (analysis: string, event: string) => Promise<*>,
- onClose: () => void
-};
-
-function RemoveVersionForm(props: Props) {
- return (
- <RemoveEventForm
- {...props}
- removeEventButtonText="project_activity.remove_version"
- removeEventQuestion="project_activity.remove_version.question"
- />
- );
-}
-
-const mapStateToProps = null;
-
-const mapDispatchToProps = { deleteEvent };
-
-export default connect(mapStateToProps, mapDispatchToProps)(RemoveVersionForm);
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/routes.js b/server/sonar-web/src/main/js/apps/projectActivity/routes.js
index 3c5a57d10ec..e11d7fbe8b4 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/routes.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/routes.js
@@ -21,7 +21,7 @@ const routes = [
{
getIndexRoute(_, callback) {
require.ensure([], require =>
- callback(null, { component: require('./components/ProjectActivityApp').default })
+ callback(null, { component: require('./components/ProjectActivityAppContainer').default })
);
}
}
diff --git a/server/sonar-web/src/main/js/store/projectActivity/paging.js b/server/sonar-web/src/main/js/apps/projectActivity/types.js
index bf14686fa9c..b3d8211dfc8 100644
--- a/server/sonar-web/src/main/js/store/projectActivity/paging.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/types.js
@@ -18,16 +18,27 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
-import type { Paging, ReceiveProjectActivityAction } from './duck';
-export type State = {
- [key: string]: Paging
+export type Event = {
+ key: string,
+ name: string,
+ category: string,
+ description?: string
};
-export default (state: State = {}, action: ReceiveProjectActivityAction): State => {
- if (action.type === 'RECEIVE_PROJECT_ACTIVITY') {
- return { ...state, [action.project]: action.paging };
- }
+export type Analysis = {
+ key: string,
+ date: string,
+ events: Array<Event>
+};
+
+export type Paging = {
+ pageIndex: number,
+ pageSize: number,
+ total: number
+};
- return state;
+export type Query = {
+ project: string,
+ category: string
};
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeVersionForm.js b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
index 8eb2192cafc..be1646db137 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeVersionForm.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
@@ -18,24 +18,23 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
-import React from 'react';
-import { connect } from 'react-redux';
-import ChangeEventForm from './ChangeEventForm';
-import { changeEvent } from '../../actions';
-import type { Event } from '../../../../store/projectActivity/duck';
+import { cleanQuery, parseAsString, serializeString } from '../../helpers/query';
+import type { Query } from './types';
+import type { RawQuery } from '../../helpers/query';
-type Props = {
- changeEvent: (event: string, name: string) => Promise<*>,
- event: Event,
- onClose: () => void
-};
+export const parseQuery = (urlQuery: RawQuery): Query => ({
+ project: parseAsString(urlQuery['id']),
+ category: parseAsString(urlQuery['category'])
+});
-function ChangeVersionForm(props: Props) {
- return <ChangeEventForm {...props} changeEventButtonText="project_activity.change_version" />;
-}
+export const serializeQuery = (query: Query): Query =>
+ cleanQuery({
+ project: serializeString(query.project),
+ category: serializeString(query.category)
+ });
-const mapStateToProps = null;
-
-const mapDispatchToProps = { changeEvent };
-
-export default connect(mapStateToProps, mapDispatchToProps)(ChangeVersionForm);
+export const serializeUrlQuery = (query: Query): RawQuery =>
+ cleanQuery({
+ id: serializeString(query.project),
+ category: serializeString(query.category)
+ });
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
index 746a6fee39e..13078f320a5 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
@@ -28,11 +28,12 @@ import VisualizationsContainer from '../visualizations/VisualizationsContainer';
import { parseUrlQuery } from '../store/utils';
import { translate } from '../../../helpers/l10n';
import * as utils from '../utils';
+import type { RawQuery } from '../../../helpers/query';
import '../styles.css';
type Props = {|
isFavorite: boolean,
- location: { pathname: string, query: { [string]: string } },
+ location: { pathname: string, query: RawQuery },
fetchProjects: (query: string, isFavorite: boolean, organization?: {}) => Promise<*>,
organization?: { key: string },
router: {
@@ -43,7 +44,7 @@ type Props = {|
|};
type State = {
- query: { [string]: string }
+ query: RawQuery
};
export default class AllProjects extends React.PureComponent {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
index f3e39df8b4d..bab378af29e 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
@@ -25,12 +25,13 @@ import AllProjectsContainer from './AllProjectsContainer';
import { getCurrentUser } from '../../../store/rootReducer';
import { isFavoriteSet, isAllSet } from '../utils';
import { searchProjects } from '../../../api/components';
+import type { RawQuery } from '../../../helpers/query';
type Props = {
currentUser: { isLoggedIn: boolean },
location: { query: {} },
router: {
- replace: (location: { pathname?: string, query?: { [string]: string } }) => void
+ replace: (location: { pathname?: string, query?: RawQuery }) => void
}
};
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
index 45ed4b6e8a8..77cd3f97cc8 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
@@ -22,13 +22,14 @@ import React from 'react';
import { IndexLink, Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
import { saveAll, saveFavorite } from '../utils';
+import type { RawQuery } from '../../../helpers/query';
type Props = {
user: {
isLoggedIn?: boolean
},
organization?: { key: string },
- query: { [string]: string }
+ query: RawQuery
};
export default class FavoriteFilter extends React.PureComponent {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js
index dc1aa2de123..356690901e6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js
@@ -25,6 +25,7 @@ import Tooltip from '../../../components/controls/Tooltip';
import PerspectiveSelect from './PerspectiveSelect';
import ProjectsSortingSelect from './ProjectsSortingSelect';
import { translate } from '../../../helpers/l10n';
+import type { RawQuery } from '../../../helpers/query';
type Props = {|
currentUser?: { isLoggedIn: boolean },
@@ -33,7 +34,7 @@ type Props = {|
organization?: { key: string },
projects: Array<*>,
projectsAppState: { loading: boolean, total?: number },
- query: { [string]: string },
+ query: RawQuery,
onSortChange: (sort: string, desc: boolean) => void,
selectedSort: string,
view: string,
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
index 9771b00f497..65fa894d22c 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
@@ -37,11 +37,12 @@ import SecurityFilter from '../filters/SecurityFilter';
import SizeFilter from '../filters/SizeFilter';
import TagsFilterContainer from '../filters/TagsFilterContainer';
import { translate } from '../../../helpers/l10n';
+import type { RawQuery } from '../../../helpers/query';
type Props = {
isFavorite: boolean,
organization?: { key: string },
- query: { [string]: string },
+ query: RawQuery,
view: string,
visualization: string
};
diff --git a/server/sonar-web/src/main/js/apps/projects/store/utils.js b/server/sonar-web/src/main/js/apps/projects/store/utils.js
index 4c383fe8d0c..190facb928c 100644
--- a/server/sonar-web/src/main/js/apps/projects/store/utils.js
+++ b/server/sonar-web/src/main/js/apps/projects/store/utils.js
@@ -34,6 +34,7 @@ const getAsLevel = value => {
return null;
};
+// TODO Maybe use parseAsString form helpers/query
const getAsString = value => {
if (!value) {
return null;
@@ -41,6 +42,7 @@ const getAsString = value => {
return value;
};
+// TODO Maybe move it to helpers/query
const getAsArray = (values, elementGetter) => {
if (!values) {
return null;
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/__snapshots__/query-test.js.snap b/server/sonar-web/src/main/js/helpers/__tests__/__snapshots__/query-test.js.snap
new file mode 100644
index 00000000000..55b274979e3
--- /dev/null
+++ b/server/sonar-web/src/main/js/helpers/__tests__/__snapshots__/query-test.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`cleanQuery should remove undefined and null query items 1`] = `
+Object {
+ "a": "b",
+ "d": "",
+ "e": 0,
+}
+`;
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/query-test.js b/server/sonar-web/src/main/js/helpers/__tests__/query-test.js
new file mode 100644
index 00000000000..5a60b4a52c3
--- /dev/null
+++ b/server/sonar-web/src/main/js/helpers/__tests__/query-test.js
@@ -0,0 +1,84 @@
+/*
+ * 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 query from '../query';
+
+describe('queriesEqual', () => {
+ it('should correctly test equality of two queries', () => {
+ expect(query.queriesEqual({ a: 'test', b: 'test' }, { a: 'test', b: 'test' })).toBeTruthy();
+ expect(query.queriesEqual({ a: [1, 2], b: 'test' }, { a: [1, 2], b: 'test' })).toBeTruthy();
+ expect(query.queriesEqual({ a: 'a' }, { a: 'test', b: 'test' })).toBeFalsy();
+ expect(query.queriesEqual({ a: [1, 2], b: 'test' }, { a: [1], b: 'test' })).toBeFalsy();
+ });
+});
+
+describe('cleanQuery', () => {
+ it('should remove undefined and null query items', () => {
+ expect(query.cleanQuery({ a: 'b', b: undefined, c: null, d: '', e: 0 })).toMatchSnapshot();
+ });
+});
+
+describe('parseAsBoolean', () => {
+ it('should parse booleans correctly', () => {
+ expect(query.parseAsBoolean('false')).toBeFalsy();
+ expect(query.parseAsBoolean('true')).toBeTruthy();
+ });
+
+ it('should return a default value', () => {
+ expect(query.parseAsBoolean(1)).toBeTruthy();
+ expect(query.parseAsBoolean('foo')).toBeTruthy();
+ });
+});
+
+describe('parseAsFacetMode', () => {
+ it('should facets modes correctly', () => {
+ expect(query.parseAsFacetMode('debt')).toBe('effort');
+ expect(query.parseAsFacetMode('effort')).toBe('effort');
+ expect(query.parseAsFacetMode('count')).toBe('count');
+ expect(query.parseAsFacetMode('random')).toBe('count');
+ });
+});
+
+describe('parseAsString', () => {
+ it('should parse strings correctly', () => {
+ expect(query.parseAsString('random')).toBe('random');
+ expect(query.parseAsString('')).toBe('');
+ expect(query.parseAsString(null)).toBe('');
+ });
+});
+
+describe('parseAsArray', () => {
+ it('should parse string arrays correctly', () => {
+ expect(query.parseAsArray('1,2,3', query.parseAsString)).toEqual(['1', '2', '3']);
+ });
+});
+
+describe('serializeString', () => {
+ it('should serialize string correctly', () => {
+ expect(query.serializeString('foo')).toBe('foo');
+ expect(query.serializeString('')).toBeUndefined();
+ });
+});
+
+describe('serializeStringArray', () => {
+ it('should serialize array of string correctly', () => {
+ expect(query.serializeStringArray(['1', '2', '3'])).toBe('1,2,3');
+ expect(query.serializeStringArray([])).toBeUndefined();
+ });
+});
diff --git a/server/sonar-web/src/main/js/helpers/query.js b/server/sonar-web/src/main/js/helpers/query.js
new file mode 100644
index 00000000000..6e7746d4b08
--- /dev/null
+++ b/server/sonar-web/src/main/js/helpers/query.js
@@ -0,0 +1,68 @@
+/*
+ * 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 { isNil, omitBy } from 'lodash';
+
+export type RawQuery = { [string]: string };
+
+const arraysEqual = <T>(a: Array<T>, b: Array<T>) => {
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+export const queriesEqual = <T>(a: T, b: T): boolean => {
+ const keysA = Object.keys(a);
+ const keysB = Object.keys(b);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ }
+
+ return keysA.every(
+ key =>
+ (Array.isArray(a[key]) && Array.isArray(b[key])
+ ? arraysEqual(a[key], b[key])
+ : a[key] === b[key])
+ );
+};
+
+export const cleanQuery = (query: { [string]: ?string }): RawQuery => omitBy(query, isNil);
+
+export const parseAsBoolean = (value: ?string, defaultValue: boolean = true): boolean =>
+ (value === 'false' ? false : value === 'true' ? true : defaultValue);
+
+export const parseAsFacetMode = (facetMode: string) =>
+ (facetMode === 'debt' || facetMode === 'effort' ? 'effort' : 'count');
+
+export const parseAsString = (value: ?string): string => value || '';
+
+export const parseAsArray = <T>(value: ?string, itemParser: string => T): Array<T> =>
+ (value ? value.split(',').map(itemParser) : []);
+
+export const serializeString = (value: string): ?string => value || undefined;
+
+export const serializeStringArray = (value: ?Array<string>): ?string =>
+ (value && value.length ? value.join() : undefined);
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analyses-test.js.snap b/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analyses-test.js.snap
deleted file mode 100644
index 48c18d0c6e0..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analyses-test.js.snap
+++ /dev/null
@@ -1,95 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`reducer 1`] = `Object {}`;
-
-exports[`reducer 2`] = `
-Object {
- "AVgAgC1Vdo07z3PUnnkt": Object {
- "date": "2016-10-26T12:17:29+0200",
- "events": Array [
- "AVkWNYNYr4pSN7TrXcjY",
- ],
- "key": "AVgAgC1Vdo07z3PUnnkt",
- },
- "AVgFqeOSKpGuA48ADATE": Object {
- "date": "2016-10-27T12:21:15+0200",
- "events": Array [],
- "key": "AVgFqeOSKpGuA48ADATE",
- },
- "AVgGkRvCrrTJiPpCD-rG": Object {
- "date": "2016-10-27T16:33:50+0200",
- "events": Array [
- "AVjUDBiSiXOcXjpycvde",
- ],
- "key": "AVgGkRvCrrTJiPpCD-rG",
- },
-}
-`;
-
-exports[`reducer 3`] = `
-Object {
- "AVgAgC1Vdo07z3PUnnkt": Object {
- "date": "2016-10-26T12:17:29+0200",
- "events": Array [
- "AVkWNYNYr4pSN7TrXcjY",
- ],
- "key": "AVgAgC1Vdo07z3PUnnkt",
- },
- "AVgFqeOSKpGuA48ADATE": Object {
- "date": "2016-10-27T12:21:15+0200",
- "events": Array [],
- "key": "AVgFqeOSKpGuA48ADATE",
- },
- "AVgGkRvCrrTJiPpCD-rG": Object {
- "date": "2016-10-27T16:33:50+0200",
- "events": Array [
- "AVjUDBiSiXOcXjpycvde",
- "AVkWcQ8Hr4pSN7TrXcjZ",
- ],
- "key": "AVgGkRvCrrTJiPpCD-rG",
- },
-}
-`;
-
-exports[`reducer 4`] = `
-Object {
- "AVgAgC1Vdo07z3PUnnkt": Object {
- "date": "2016-10-26T12:17:29+0200",
- "events": Array [
- "AVkWNYNYr4pSN7TrXcjY",
- ],
- "key": "AVgAgC1Vdo07z3PUnnkt",
- },
- "AVgFqeOSKpGuA48ADATE": Object {
- "date": "2016-10-27T12:21:15+0200",
- "events": Array [],
- "key": "AVgFqeOSKpGuA48ADATE",
- },
- "AVgGkRvCrrTJiPpCD-rG": Object {
- "date": "2016-10-27T16:33:50+0200",
- "events": Array [
- "AVjUDBiSiXOcXjpycvde",
- ],
- "key": "AVgGkRvCrrTJiPpCD-rG",
- },
-}
-`;
-
-exports[`reducer 5`] = `
-Object {
- "AVgAgC1Vdo07z3PUnnkt": Object {
- "date": "2016-10-26T12:17:29+0200",
- "events": Array [
- "AVkWNYNYr4pSN7TrXcjY",
- ],
- "key": "AVgAgC1Vdo07z3PUnnkt",
- },
- "AVgGkRvCrrTJiPpCD-rG": Object {
- "date": "2016-10-27T16:33:50+0200",
- "events": Array [
- "AVjUDBiSiXOcXjpycvde",
- ],
- "key": "AVgGkRvCrrTJiPpCD-rG",
- },
-}
-`;
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analysesByProject-test.js.snap b/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analysesByProject-test.js.snap
deleted file mode 100644
index 335d2124946..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/analysesByProject-test.js.snap
+++ /dev/null
@@ -1,57 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`reducer 1`] = `Object {}`;
-
-exports[`reducer 2`] = `
-Object {
- "project-foo": Array [
- "AVgFqeOSKpGuA48ADATE",
- "AVgAgC1Vdo07z3PUnnkt",
- ],
-}
-`;
-
-exports[`reducer 3`] = `
-Object {
- "project-foo": Array [
- "AVgFqeOSKpGuA48ADATE",
- "AVgAgC1Vdo07z3PUnnkt",
- "AVgFqeOSKpGuA48ADATX",
- ],
-}
-`;
-
-exports[`reducer 4`] = `
-Object {
- "project-bar": Array [
- "AVgGkRvCrrTJiPpCD-rG",
- ],
- "project-foo": Array [
- "AVgFqeOSKpGuA48ADATE",
- "AVgAgC1Vdo07z3PUnnkt",
- "AVgFqeOSKpGuA48ADATX",
- ],
-}
-`;
-
-exports[`reducer 5`] = `
-Object {
- "project-bar": Array [
- "AVgGkRvCrrTJiPpCD-rG",
- ],
- "project-foo": Array [
- "AVgAgC1Vdo07z3PUnnkt",
- "AVgFqeOSKpGuA48ADATX",
- ],
-}
-`;
-
-exports[`reducer 6`] = `
-Object {
- "project-bar": Array [],
- "project-foo": Array [
- "AVgAgC1Vdo07z3PUnnkt",
- "AVgFqeOSKpGuA48ADATX",
- ],
-}
-`;
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/duck-test.js.snap b/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/duck-test.js.snap
deleted file mode 100644
index 8dcb0729436..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/duck-test.js.snap
+++ /dev/null
@@ -1,77 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`actions addEvent 1`] = `
-Object {
- "analysis": "foo",
- "event": Object {
- "key": "bar",
- },
- "type": "ADD_PROJECT_ACTIVITY_EVENT",
-}
-`;
-
-exports[`actions changeEvent 1`] = `
-Object {
- "changes": Object {
- "name": "bar",
- },
- "event": "foo",
- "type": "CHANGE_PROJECT_ACTIVITY_EVENT",
-}
-`;
-
-exports[`actions deleteAnalysis 1`] = `
-Object {
- "analysis": "bar",
- "project": "foo",
- "type": "DELETE_PROJECT_ACTIVITY_ANALYSIS",
-}
-`;
-
-exports[`actions deleteEvent 1`] = `
-Object {
- "analysis": "foo",
- "event": "bar",
- "type": "DELETE_PROJECT_ACTIVITY_EVENT",
-}
-`;
-
-exports[`selectors getAnalyses 1`] = `
-Array [
- Object {
- "date": "2016-10-27T16:33:50+0200",
- "events": Array [
- Object {
- "category": "VERSION",
- "key": "AVjUDBiSiXOcXjpycvde",
- "name": "2.18-SNAPSHOT",
- },
- ],
- "key": "AVgGkRvCrrTJiPpCD-rG",
- },
- Object {
- "date": "2016-10-27T12:21:15+0200",
- "events": Array [],
- "key": "AVgFqeOSKpGuA48ADATE",
- },
- Object {
- "date": "2016-10-26T12:17:29+0200",
- "events": Array [
- Object {
- "category": "OTHER",
- "key": "AVkWNYNYr4pSN7TrXcjY",
- "name": "foo",
- },
- ],
- "key": "AVgAgC1Vdo07z3PUnnkt",
- },
-]
-`;
-
-exports[`selectors getPaging 1`] = `
-Object {
- "pageIndex": 1,
- "pageSize": 100,
- "total": 3,
-}
-`;
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/events-test.js.snap b/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/events-test.js.snap
deleted file mode 100644
index 284a895a928..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/events-test.js.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`reducer 1`] = `Object {}`;
-
-exports[`reducer 2`] = `
-Object {
- "AVjUDBiSiXOcXjpycvde": Object {
- "category": "VERSION",
- "key": "AVjUDBiSiXOcXjpycvde",
- "name": "2.18-SNAPSHOT",
- },
- "AVkWNYNYr4pSN7TrXcjY": Object {
- "category": "OTHER",
- "key": "AVkWNYNYr4pSN7TrXcjY",
- "name": "foo",
- },
-}
-`;
-
-exports[`reducer 3`] = `
-Object {
- "AVjUDBiSiXOcXjpycvde": Object {
- "category": "VERSION",
- "key": "AVjUDBiSiXOcXjpycvde",
- "name": "2.18-SNAPSHOT",
- },
- "AVkWNYNYr4pSN7TrXcjY": Object {
- "category": "OTHER",
- "key": "AVkWNYNYr4pSN7TrXcjY",
- "name": "foo",
- },
- "AVkWcQ8Hr4pSN7TrXcjZ": Object {
- "category": "OTHER",
- "key": "AVkWcQ8Hr4pSN7TrXcjZ",
- "name": "custom",
- },
-}
-`;
-
-exports[`reducer 4`] = `
-Object {
- "AVjUDBiSiXOcXjpycvde": Object {
- "category": "VERSION",
- "key": "AVjUDBiSiXOcXjpycvde",
- "name": "2.18-SNAPSHOT",
- },
- "AVkWNYNYr4pSN7TrXcjY": Object {
- "category": "OTHER",
- "key": "AVkWNYNYr4pSN7TrXcjY",
- "name": "foo",
- },
- "AVkWcQ8Hr4pSN7TrXcjZ": Object {
- "category": "OTHER",
- "key": "AVkWcQ8Hr4pSN7TrXcjZ",
- "name": "new name",
- },
-}
-`;
-
-exports[`reducer 5`] = `
-Object {
- "AVjUDBiSiXOcXjpycvde": Object {
- "category": "VERSION",
- "key": "AVjUDBiSiXOcXjpycvde",
- "name": "2.18-SNAPSHOT",
- },
- "AVkWNYNYr4pSN7TrXcjY": Object {
- "category": "OTHER",
- "key": "AVkWNYNYr4pSN7TrXcjY",
- "name": "foo",
- },
-}
-`;
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/paging-test.js.snap b/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/paging-test.js.snap
deleted file mode 100644
index 8698272003a..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/__snapshots__/paging-test.js.snap
+++ /dev/null
@@ -1,23 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`reducer 1`] = `Object {}`;
-
-exports[`reducer 2`] = `
-Object {
- "project-foo": Object {
- "pageIndex": 1,
- "pageSize": 100,
- "total": 3,
- },
-}
-`;
-
-exports[`reducer 3`] = `
-Object {
- "project-foo": Object {
- "pageIndex": 2,
- "pageSize": 30,
- "total": 5,
- },
-}
-`;
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/analyses-test.js b/server/sonar-web/src/main/js/store/projectActivity/__tests__/analyses-test.js
deleted file mode 100644
index b2571ab2c90..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/analyses-test.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 { configureTestStore } from '../../utils/configureStore';
-import analyses, { getAnalysis } from '../analyses';
-import { receiveProjectActivity, addEvent, deleteEvent, deleteAnalysis } from '../duck';
-
-const PROJECT = 'project-foo';
-
-const ANALYSES = [
- {
- key: 'AVgGkRvCrrTJiPpCD-rG',
- date: '2016-10-27T16:33:50+0200',
- events: [
- {
- key: 'AVjUDBiSiXOcXjpycvde',
- category: 'VERSION',
- name: '2.18-SNAPSHOT'
- }
- ]
- },
- {
- key: 'AVgFqeOSKpGuA48ADATE',
- date: '2016-10-27T12:21:15+0200',
- events: []
- },
- {
- key: 'AVgAgC1Vdo07z3PUnnkt',
- date: '2016-10-26T12:17:29+0200',
- events: [
- {
- key: 'AVkWNYNYr4pSN7TrXcjY',
- category: 'OTHER',
- name: 'foo'
- }
- ]
- }
-];
-
-const PAGING = {
- total: 3,
- pageIndex: 1,
- pageSize: 100
-};
-
-const NEW_EVENT = {
- key: 'AVkWcQ8Hr4pSN7TrXcjZ',
- category: 'OTHER',
- name: 'custom'
-};
-
-it('reducer', () => {
- const store = configureTestStore(analyses);
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT, ANALYSES, PAGING));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(addEvent(ANALYSES[0].key, NEW_EVENT));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(deleteEvent(ANALYSES[0].key, NEW_EVENT.key));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(deleteAnalysis(PROJECT, ANALYSES[1].key));
- expect(store.getState()).toMatchSnapshot();
-});
-
-it('selector `getAnalysis`', () => {
- const analysis = ANALYSES[0];
- const store = configureTestStore(analyses, { [analysis.key]: analysis });
- expect(getAnalysis(store.getState(), analysis.key)).toBe(analysis);
- expect(getAnalysis(store.getState(), 'random')).toBeFalsy();
-});
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/analysesByProject-test.js b/server/sonar-web/src/main/js/store/projectActivity/__tests__/analysesByProject-test.js
deleted file mode 100644
index 8c4a00bf0e2..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/analysesByProject-test.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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 { configureTestStore } from '../../utils/configureStore';
-import analysesByProject from '../analysesByProject';
-import { receiveProjectActivity, deleteAnalysis } from '../duck';
-
-const PROJECT_FOO = 'project-foo';
-const PROJECT_BAR = 'project-bar';
-
-const ANALYSES_FOO = [
- {
- key: 'AVgFqeOSKpGuA48ADATE',
- date: '2016-10-27T12:21:15+0200',
- events: []
- },
- {
- key: 'AVgAgC1Vdo07z3PUnnkt',
- date: '2016-10-26T12:17:29+0200',
- events: []
- }
-];
-
-const ANALYSES_FOO_2 = [
- {
- key: 'AVgFqeOSKpGuA48ADATX',
- date: '2016-10-27T12:21:15+0200',
- events: []
- }
-];
-
-const ANALYSES_BAR = [
- {
- key: 'AVgGkRvCrrTJiPpCD-rG',
- date: '2016-10-27T16:33:50+0200',
- events: []
- }
-];
-
-const PAGING = {
- total: 3,
- pageIndex: 1,
- pageSize: 100
-};
-
-it('reducer', () => {
- const store = configureTestStore(analysesByProject);
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT_FOO, ANALYSES_FOO, PAGING));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT_FOO, ANALYSES_FOO_2, { pageIndex: 2 }));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT_BAR, ANALYSES_BAR, PAGING));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(deleteAnalysis(PROJECT_FOO, 'AVgFqeOSKpGuA48ADATE'));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(deleteAnalysis(PROJECT_BAR, 'AVgGkRvCrrTJiPpCD-rG'));
- expect(store.getState()).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/duck-test.js b/server/sonar-web/src/main/js/store/projectActivity/__tests__/duck-test.js
deleted file mode 100644
index d1a6ddfd4a9..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/duck-test.js
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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 { configureTestStore } from '../../utils/configureStore';
-import reducer, {
- receiveProjectActivity,
- getAnalyses,
- getPaging,
- addEvent,
- changeEvent,
- deleteEvent,
- deleteAnalysis
-} from '../duck';
-
-const PROJECT = 'project-foo';
-
-const ANALYSES = [
- {
- key: 'AVgGkRvCrrTJiPpCD-rG',
- date: '2016-10-27T16:33:50+0200',
- events: [
- {
- key: 'AVjUDBiSiXOcXjpycvde',
- category: 'VERSION',
- name: '2.18-SNAPSHOT'
- }
- ]
- },
- {
- key: 'AVgFqeOSKpGuA48ADATE',
- date: '2016-10-27T12:21:15+0200',
- events: []
- },
- {
- key: 'AVgAgC1Vdo07z3PUnnkt',
- date: '2016-10-26T12:17:29+0200',
- events: [
- {
- key: 'AVkWNYNYr4pSN7TrXcjY',
- category: 'OTHER',
- name: 'foo'
- }
- ]
- }
-];
-
-const PAGING = {
- total: 3,
- pageIndex: 1,
- pageSize: 100
-};
-
-describe('actions', () => {
- it('addEvent', () => {
- expect(addEvent('foo', { key: 'bar' })).toMatchSnapshot();
- });
-
- it('changeEvent', () => {
- expect(changeEvent('foo', { name: 'bar' })).toMatchSnapshot();
- });
-
- it('deleteEvent', () => {
- expect(deleteEvent('foo', 'bar')).toMatchSnapshot();
- });
-
- it('deleteAnalysis', () => {
- expect(deleteAnalysis('foo', 'bar')).toMatchSnapshot();
- });
-});
-
-describe('selectors', () => {
- it('getAnalyses', () => {
- const store = configureTestStore(reducer);
- store.dispatch(receiveProjectActivity(PROJECT, ANALYSES, PAGING));
- expect(getAnalyses(store.getState(), PROJECT)).toMatchSnapshot();
- });
-
- it('getPaging', () => {
- const store = configureTestStore(reducer);
- store.dispatch(receiveProjectActivity(PROJECT, ANALYSES, PAGING));
- expect(getPaging(store.getState(), PROJECT)).toMatchSnapshot();
- });
-});
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/events-test.js b/server/sonar-web/src/main/js/store/projectActivity/__tests__/events-test.js
deleted file mode 100644
index 87b1589cb25..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/events-test.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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 { configureTestStore } from '../../utils/configureStore';
-import events, { getEvent } from '../events';
-import { receiveProjectActivity, addEvent, changeEvent, deleteEvent } from '../duck';
-
-const PROJECT = 'project-foo';
-
-const ANALYSES = [
- {
- key: 'AVgGkRvCrrTJiPpCD-rG',
- date: '2016-10-27T16:33:50+0200',
- events: [
- {
- key: 'AVjUDBiSiXOcXjpycvde',
- category: 'VERSION',
- name: '2.18-SNAPSHOT'
- }
- ]
- },
- {
- key: 'AVgFqeOSKpGuA48ADATE',
- date: '2016-10-27T12:21:15+0200',
- events: []
- },
- {
- key: 'AVgAgC1Vdo07z3PUnnkt',
- date: '2016-10-26T12:17:29+0200',
- events: [
- {
- key: 'AVkWNYNYr4pSN7TrXcjY',
- category: 'OTHER',
- name: 'foo'
- }
- ]
- }
-];
-
-const PAGING = {
- total: 3,
- pageIndex: 1,
- pageSize: 100
-};
-
-const NEW_EVENT = {
- key: 'AVkWcQ8Hr4pSN7TrXcjZ',
- category: 'OTHER',
- name: 'custom'
-};
-
-it('reducer', () => {
- const store = configureTestStore(events);
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT, ANALYSES, PAGING));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(addEvent(ANALYSES[0].key, NEW_EVENT));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(changeEvent(NEW_EVENT.key, { name: 'new name' }));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(deleteEvent(ANALYSES[0].key, NEW_EVENT.key));
- expect(store.getState()).toMatchSnapshot();
-});
-
-it('selector `getEvent`', () => {
- const event = ANALYSES[0].events[0];
- const store = configureTestStore(events, { [event.key]: event });
- expect(getEvent(store.getState(), event.key)).toBe(event);
- expect(getEvent(store.getState(), 'random')).toBeFalsy();
-});
diff --git a/server/sonar-web/src/main/js/store/projectActivity/__tests__/paging-test.js b/server/sonar-web/src/main/js/store/projectActivity/__tests__/paging-test.js
deleted file mode 100644
index c100f66ad4d..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/__tests__/paging-test.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 { configureTestStore } from '../../utils/configureStore';
-import paging from '../paging';
-import { receiveProjectActivity } from '../duck';
-
-const PROJECT = 'project-foo';
-
-const ANALYSES = [];
-
-const PAGING_1 = {
- total: 3,
- pageIndex: 1,
- pageSize: 100
-};
-
-const PAGING_2 = {
- total: 5,
- pageIndex: 2,
- pageSize: 30
-};
-
-it('reducer', () => {
- const store = configureTestStore(paging);
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT, ANALYSES, PAGING_1));
- expect(store.getState()).toMatchSnapshot();
-
- store.dispatch(receiveProjectActivity(PROJECT, ANALYSES, PAGING_2));
- expect(store.getState()).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/store/projectActivity/analyses.js b/server/sonar-web/src/main/js/store/projectActivity/analyses.js
deleted file mode 100644
index 1bed62ad651..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/analyses.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 { keyBy } from 'lodash';
-import type {
- Action,
- ReceiveProjectActivityAction,
- AddEventAction,
- DeleteEventAction,
- DeleteAnalysisAction
-} from './duck';
-
-type Analysis = {
- key: string,
- date: string,
- events: Array<string>
-};
-
-export type State = {
- [key: string]: Analysis
-};
-
-const receiveProjectActivity = (state: State, action: ReceiveProjectActivityAction): State => {
- const analysesWithFlatEvents = action.analyses.map(analysis => ({
- ...analysis,
- events: analysis.events.map(event => event.key)
- }));
- return { ...state, ...keyBy(analysesWithFlatEvents, 'key') };
-};
-
-const addEvent = (state: State, action: AddEventAction): State => {
- const analysis = state[action.analysis];
- const newAnalysis = {
- ...analysis,
- events: [...analysis.events, action.event.key]
- };
- return { ...state, [action.analysis]: newAnalysis };
-};
-
-const deleteEvent = (state: State, action: DeleteEventAction): State => {
- const analysis = state[action.analysis];
- const newAnalysis = {
- ...analysis,
- events: analysis.events.filter(event => event !== action.event)
- };
- return { ...state, [action.analysis]: newAnalysis };
-};
-
-const deleteAnalysis = (state: State, action: DeleteAnalysisAction): State => {
- const newState = { ...state };
- delete newState[action.analysis];
- return newState;
-};
-
-export default (state: State = {}, action: Action): State => {
- switch (action.type) {
- case 'RECEIVE_PROJECT_ACTIVITY':
- return receiveProjectActivity(state, action);
- case 'ADD_PROJECT_ACTIVITY_EVENT':
- return addEvent(state, action);
- case 'DELETE_PROJECT_ACTIVITY_EVENT':
- return deleteEvent(state, action);
- case 'DELETE_PROJECT_ACTIVITY_ANALYSIS':
- return deleteAnalysis(state, action);
- default:
- return state;
- }
-};
-
-export const getAnalysis = (state: State, key: string): Analysis => state[key];
diff --git a/server/sonar-web/src/main/js/store/projectActivity/analysesByProject.js b/server/sonar-web/src/main/js/store/projectActivity/analysesByProject.js
deleted file mode 100644
index 8601d2d0873..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/analysesByProject.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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 type { Action, ReceiveProjectActivityAction, DeleteAnalysisAction } from './duck';
-
-export type State = {
- [key: string]: Array<string>
-};
-
-const receiveProjectActivity = (state: State, action: ReceiveProjectActivityAction): State => {
- const analyses = state[action.project] || [];
- const newAnalyses = action.analyses.map(analysis => analysis.key);
- return {
- ...state,
- [action.project]: action.paging.pageIndex === 1 ? newAnalyses : [...analyses, ...newAnalyses]
- };
-};
-
-const deleteAnalysis = (state: State, action: DeleteAnalysisAction): State => {
- const analyses = state[action.project];
- return {
- ...state,
- [action.project]: analyses.filter(key => key !== action.analysis)
- };
-};
-
-export default (state: State = {}, action: Action): State => {
- switch (action.type) {
- case 'RECEIVE_PROJECT_ACTIVITY':
- return receiveProjectActivity(state, action);
- case 'DELETE_PROJECT_ACTIVITY_ANALYSIS':
- return deleteAnalysis(state, action);
- default:
- return state;
- }
-};
diff --git a/server/sonar-web/src/main/js/store/projectActivity/duck.js b/server/sonar-web/src/main/js/store/projectActivity/duck.js
deleted file mode 100644
index 75e24c7e839..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/duck.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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 { combineReducers } from 'redux';
-import analyses, * as fromAnalyses from './analyses';
-import type { State as AnalysesState } from './analyses';
-import analysesByProject from './analysesByProject';
-import type { State as AnalysesByProjectState } from './analysesByProject';
-import events, * as fromEvents from './events';
-import type { State as EventsState } from './events';
-import paging from './paging';
-import type { State as PagingState } from './paging';
-
-export type Event = {
- key: string,
- name: string,
- category: string,
- description?: string
-};
-
-export type Analysis = {
- key: string,
- date: string,
- events: Array<Event>
-};
-
-export type Paging = {
- total: number,
- pageIndex: number,
- pageSize: number
-};
-
-export type ReceiveProjectActivityAction = {
- type: 'RECEIVE_PROJECT_ACTIVITY',
- project: string,
- analyses: Array<Analysis>,
- paging: Paging
-};
-
-export type AddEventAction = {
- type: 'ADD_PROJECT_ACTIVITY_EVENT',
- analysis: string,
- event: Event
-};
-
-export type DeleteEventAction = {
- type: 'DELETE_PROJECT_ACTIVITY_EVENT',
- analysis: string,
- event: string
-};
-
-export type ChangeEventAction = {
- type: 'CHANGE_PROJECT_ACTIVITY_EVENT',
- event: string,
- changes: Object
-};
-
-export type DeleteAnalysisAction = {
- type: 'DELETE_PROJECT_ACTIVITY_ANALYSIS',
- project: string,
- analysis: string
-};
-
-export type Action =
- | ReceiveProjectActivityAction
- | AddEventAction
- | DeleteEventAction
- | ChangeEventAction
- | DeleteAnalysisAction;
-
-export const receiveProjectActivity = (
- project: string,
- analyses: Array<Analysis>,
- paging: Paging
-): ReceiveProjectActivityAction => ({
- type: 'RECEIVE_PROJECT_ACTIVITY',
- project,
- analyses,
- paging
-});
-
-export const addEvent = (analysis: string, event: Event): AddEventAction => ({
- type: 'ADD_PROJECT_ACTIVITY_EVENT',
- analysis,
- event
-});
-
-export const deleteEvent = (analysis: string, event: string): DeleteEventAction => ({
- type: 'DELETE_PROJECT_ACTIVITY_EVENT',
- analysis,
- event
-});
-
-export const changeEvent = (event: string, changes: Object): ChangeEventAction => ({
- type: 'CHANGE_PROJECT_ACTIVITY_EVENT',
- event,
- changes
-});
-
-export const deleteAnalysis = (project: string, analysis: string): DeleteAnalysisAction => ({
- type: 'DELETE_PROJECT_ACTIVITY_ANALYSIS',
- project,
- analysis
-});
-
-type State = {
- analyses: AnalysesState,
- analysesByProject: AnalysesByProjectState,
- events: EventsState,
- filter: string,
- paging: PagingState
-};
-
-export default combineReducers({ analyses, analysesByProject, events, paging });
-
-const getEvent = (state: State, key: string): Event => fromEvents.getEvent(state.events, key);
-
-const getAnalysis = (state: State, key: string) => {
- const analysis = fromAnalyses.getAnalysis(state.analyses, key);
- const events: Array<Event> = analysis.events.map(key => getEvent(state, key));
- return { ...analysis, events };
-};
-
-export const getAnalyses = (state: State, project: string) =>
- state.analysesByProject[project] &&
- state.analysesByProject[project].map(key => getAnalysis(state, key));
-export const getPaging = (state: State, project: string) => state.paging[project];
diff --git a/server/sonar-web/src/main/js/store/projectActivity/events.js b/server/sonar-web/src/main/js/store/projectActivity/events.js
deleted file mode 100644
index d4024b2e068..00000000000
--- a/server/sonar-web/src/main/js/store/projectActivity/events.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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 { keyBy } from 'lodash';
-import type {
- Action,
- ReceiveProjectActivityAction,
- AddEventAction,
- DeleteEventAction,
- ChangeEventAction
-} from './duck';
-
-export type State = {
- [key: string]: {
- key: string,
- name: string,
- category: string,
- description?: string
- }
-};
-
-const receiveProjectActivity = (state: State, action: ReceiveProjectActivityAction): State => {
- const events = {};
- action.analyses.forEach(analysis => {
- Object.assign(events, keyBy(analysis.events, 'key'));
- });
- return { ...state, ...events };
-};
-
-const addEvent = (state: State, action: AddEventAction): State => {
- return { ...state, [action.event.key]: action.event };
-};
-
-const deleteEvent = (state: State, action: DeleteEventAction): State => {
- const newState = { ...state };
- delete newState[action.event];
- return newState;
-};
-
-const changeEvent = (state: State, action: ChangeEventAction): State => {
- const newEvent = { ...state[action.event], ...action.changes };
- return { ...state, [action.event]: newEvent };
-};
-
-export default (state: State = {}, action: Action): State => {
- switch (action.type) {
- case 'RECEIVE_PROJECT_ACTIVITY':
- return receiveProjectActivity(state, action);
- case 'ADD_PROJECT_ACTIVITY_EVENT':
- return addEvent(state, action);
- case 'DELETE_PROJECT_ACTIVITY_EVENT':
- return deleteEvent(state, action);
- case 'CHANGE_PROJECT_ACTIVITY_EVENT':
- return changeEvent(state, action);
- default:
- return state;
- }
-};
-
-export const getEvent = (state: State, key: string) => state[key];
diff --git a/server/sonar-web/src/main/js/store/rootReducer.js b/server/sonar-web/src/main/js/store/rootReducer.js
index 5404ef78f6a..a0f81547c53 100644
--- a/server/sonar-web/src/main/js/store/rootReducer.js
+++ b/server/sonar-web/src/main/js/store/rootReducer.js
@@ -28,7 +28,6 @@ import notifications, * as fromNotifications from './notifications/duck';
import organizations, * as fromOrganizations from './organizations/duck';
import organizationsMembers, * as fromOrganizationsMembers from './organizationsMembers/reducer';
import globalMessages, * as fromGlobalMessages from './globalMessages/duck';
-import projectActivity from './projectActivity/duck';
import measuresApp, * as fromMeasuresApp from '../apps/component-measures/store/rootReducer';
import permissionsApp, * as fromPermissionsApp from '../apps/permissions/shared/store/rootReducer';
import projectAdminApp, * as fromProjectAdminApp from '../apps/project-admin/store/rootReducer';
@@ -46,7 +45,6 @@ export default combineReducers({
notifications,
organizations,
organizationsMembers,
- projectActivity,
users,
// apps
@@ -122,8 +120,6 @@ export const getOrganizationMembersLogins = (state, organization) =>
export const getOrganizationMembersState = (state, organization) =>
fromOrganizationsMembers.getOrganizationMembersState(state.organizationsMembers, organization);
-export const getProjectActivity = state => state.projectActivity;
-
export const getProjects = state => fromProjectsApp.getProjects(state.projectsApp);
export const getProjectsAppState = state => fromProjectsApp.getState(state.projectsApp);