aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/store
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-09-03 09:21:22 +0200
committerSonarTech <sonartech@sonarsource.com>2018-09-03 20:20:51 +0200
commitf6fd6fb056ca6c3de3637cfeafb53c30815ee12d (patch)
treebdf6b3a660066ffe6aaf644f6969e4353a7e21c8 /server/sonar-web/src/main/js/store
parentf332f24ea986de3267aedcc9ba8ec1441ff4226b (diff)
downloadsonarqube-f6fd6fb056ca6c3de3637cfeafb53c30815ee12d.tar.gz
sonarqube-f6fd6fb056ca6c3de3637cfeafb53c30815ee12d.zip
finish typing redux store and simplify connected components (#675)
Diffstat (limited to 'server/sonar-web/src/main/js/store')
-rw-r--r--server/sonar-web/src/main/js/store/__tests__/__snapshots__/organizations-test.ts.snap97
-rw-r--r--server/sonar-web/src/main/js/store/__tests__/organizations-test.ts103
-rw-r--r--server/sonar-web/src/main/js/store/appState.ts (renamed from server/sonar-web/src/main/js/store/appState/duck.ts)40
-rw-r--r--server/sonar-web/src/main/js/store/globalMessages.ts102
-rw-r--r--server/sonar-web/src/main/js/store/globalMessages/duck.js131
-rw-r--r--server/sonar-web/src/main/js/store/languages.ts (renamed from server/sonar-web/src/main/js/store/languages/reducer.ts)24
-rw-r--r--server/sonar-web/src/main/js/store/metrics.ts (renamed from server/sonar-web/src/main/js/store/metrics/reducer.js)36
-rw-r--r--server/sonar-web/src/main/js/store/metrics/actions.js40
-rw-r--r--server/sonar-web/src/main/js/store/organizations.ts116
-rw-r--r--server/sonar-web/src/main/js/store/organizations/__tests__/__snapshots__/duck-test.ts.snap43
-rw-r--r--server/sonar-web/src/main/js/store/organizations/__tests__/duck-test.ts61
-rw-r--r--server/sonar-web/src/main/js/store/organizations/duck.ts199
-rw-r--r--server/sonar-web/src/main/js/store/rootActions.js67
-rw-r--r--server/sonar-web/src/main/js/store/rootActions.ts76
-rw-r--r--server/sonar-web/src/main/js/store/rootReducer.js114
-rw-r--r--server/sonar-web/src/main/js/store/rootReducer.ts166
-rw-r--r--server/sonar-web/src/main/js/store/users.ts105
-rw-r--r--server/sonar-web/src/main/js/store/users/actions.ts49
-rw-r--r--server/sonar-web/src/main/js/store/users/reducer.ts71
-rw-r--r--server/sonar-web/src/main/js/store/utils/actions.ts (renamed from server/sonar-web/src/main/js/store/languages/actions.js)9
-rw-r--r--server/sonar-web/src/main/js/store/utils/configureStore.ts (renamed from server/sonar-web/src/main/js/store/utils/configureStore.js)8
21 files changed, 828 insertions, 829 deletions
diff --git a/server/sonar-web/src/main/js/store/__tests__/__snapshots__/organizations-test.ts.snap b/server/sonar-web/src/main/js/store/__tests__/__snapshots__/organizations-test.ts.snap
new file mode 100644
index 00000000000..bc2ba11d2bc
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/__tests__/__snapshots__/organizations-test.ts.snap
@@ -0,0 +1,97 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Reducer should create organization 1`] = `
+Object {
+ "byKey": Object {
+ "foo": Object {
+ "isAdmin": true,
+ "key": "foo",
+ "name": "foo",
+ },
+ },
+ "my": Array [
+ "foo",
+ ],
+}
+`;
+
+exports[`Reducer should delete organization 1`] = `
+Object {
+ "byKey": Object {},
+ "my": Array [],
+}
+`;
+
+exports[`Reducer should have initial state 1`] = `
+Object {
+ "byKey": Object {},
+ "my": Array [],
+}
+`;
+
+exports[`Reducer should receive my organizations 1`] = `
+Object {
+ "byKey": Object {
+ "bar": Object {
+ "key": "bar",
+ "name": "Bar",
+ },
+ "foo": Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ },
+ "my": Array [
+ "foo",
+ "bar",
+ ],
+}
+`;
+
+exports[`Reducer should receive organizations 1`] = `
+Object {
+ "byKey": Object {
+ "bar": Object {
+ "key": "bar",
+ "name": "Bar",
+ },
+ "foo": Object {
+ "key": "foo",
+ "name": "Foo",
+ },
+ },
+ "my": Array [],
+}
+`;
+
+exports[`Reducer should receive organizations 2`] = `
+Object {
+ "byKey": Object {
+ "bar": Object {
+ "key": "bar",
+ "name": "Bar",
+ },
+ "foo": Object {
+ "key": "foo",
+ "name": "Qwe",
+ },
+ },
+ "my": Array [],
+}
+`;
+
+exports[`Reducer should update organization 1`] = `
+Object {
+ "byKey": Object {
+ "foo": Object {
+ "description": "description",
+ "isAdmin": true,
+ "key": "foo",
+ "name": "bar",
+ },
+ },
+ "my": Array [
+ "foo",
+ ],
+}
+`;
diff --git a/server/sonar-web/src/main/js/store/__tests__/organizations-test.ts b/server/sonar-web/src/main/js/store/__tests__/organizations-test.ts
new file mode 100644
index 00000000000..41233545f96
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/__tests__/organizations-test.ts
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import reducer, {
+ getOrganizationByKey,
+ areThereCustomOrganizations,
+ getMyOrganizations,
+ State,
+ receiveOrganizations,
+ receiveMyOrganizations,
+ createOrganization,
+ updateOrganization,
+ deleteOrganization
+} from '../organizations';
+
+const state0: State = { byKey: {}, my: [] };
+
+describe('Reducer', () => {
+ it('should have initial state', () => {
+ // @ts-ignore `undefined` is passed when the redux store is created,
+ // however should not be allowed by typings
+ expect(reducer(undefined, {})).toMatchSnapshot();
+ });
+
+ it('should receive organizations', () => {
+ const state1 = reducer(
+ state0,
+ receiveOrganizations([{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }])
+ );
+ expect(state1).toMatchSnapshot();
+
+ const state2 = reducer(state1, receiveOrganizations([{ key: 'foo', name: 'Qwe' }]));
+ expect(state2).toMatchSnapshot();
+ });
+
+ it('should receive my organizations', () => {
+ const state1 = reducer(
+ state0,
+ receiveMyOrganizations([{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }])
+ );
+ expect(state1).toMatchSnapshot();
+ });
+
+ it('should create organization', () => {
+ const state1 = reducer(state0, createOrganization({ key: 'foo', name: 'foo' }));
+ expect(state1).toMatchSnapshot();
+ });
+
+ it('should update organization', () => {
+ const state1 = reducer(state0, createOrganization({ key: 'foo', name: 'foo' }));
+ const state2 = reducer(
+ state1,
+ updateOrganization('foo', { name: 'bar', description: 'description' })
+ );
+ expect(state2).toMatchSnapshot();
+ });
+
+ it('should delete organization', () => {
+ const state1 = reducer(state0, createOrganization({ key: 'foo', name: 'foo' }));
+ const state2 = reducer(state1, deleteOrganization('foo'));
+ expect(state2).toMatchSnapshot();
+ });
+});
+
+describe('Selectors', () => {
+ it('getOrganizationByKey', () => {
+ const foo = { key: 'foo', name: 'Foo' };
+ const state = { ...state0, byKey: { foo } };
+ expect(getOrganizationByKey(state, 'foo')).toBe(foo);
+ expect(getOrganizationByKey(state, 'bar')).toBeFalsy();
+ });
+
+ it('getMyOrganizations', () => {
+ expect(getMyOrganizations(state0)).toEqual([]);
+
+ const foo = { key: 'foo', name: 'Foo' };
+ expect(getMyOrganizations({ ...state0, byKey: { foo }, my: ['foo'] })).toEqual([foo]);
+ });
+
+ it('areThereCustomOrganizations', () => {
+ const foo = { key: 'foo', name: 'Foo' };
+ const bar = { key: 'bar', name: 'Bar' };
+ expect(areThereCustomOrganizations({ ...state0, byKey: {} })).toBe(false);
+ expect(areThereCustomOrganizations({ ...state0, byKey: { foo } })).toBe(false);
+ expect(areThereCustomOrganizations({ ...state0, byKey: { foo, bar } })).toBe(true);
+ });
+});
diff --git a/server/sonar-web/src/main/js/store/appState/duck.ts b/server/sonar-web/src/main/js/store/appState.ts
index 79ada585879..19a5e546944 100644
--- a/server/sonar-web/src/main/js/store/appState/duck.ts
+++ b/server/sonar-web/src/main/js/store/appState.ts
@@ -17,44 +17,36 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Extension, AppState } from '../../app/types';
+import { ActionType } from './utils/actions';
+import { Extension, AppState } from '../app/types';
+import { EditionKey } from '../apps/marketplace/utils';
-interface SetAppStateAction {
- type: 'SET_APP_STATE';
- appState: AppState;
-}
-
-interface SetAdminPagesAction {
- type: 'SET_ADMIN_PAGES';
- adminPages: Extension[];
-}
-
-interface RequireAuthorizationAction {
- type: 'REQUIRE_AUTHORIZATION';
-}
-
-export type Action = SetAppStateAction | SetAdminPagesAction | RequireAuthorizationAction;
+type Action =
+ | ActionType<typeof setAppState, 'SET_APP_STATE'>
+ | ActionType<typeof setAdminPages, 'SET_ADMIN_PAGES'>
+ | ActionType<typeof requireAuthorization, 'REQUIRE_AUTHORIZATION'>;
-export function setAppState(appState: AppState): SetAppStateAction {
- return {
- type: 'SET_APP_STATE',
- appState
- };
+export function setAppState(appState: AppState) {
+ return { type: 'SET_APP_STATE', appState };
}
-export function setAdminPages(adminPages: Extension[]): SetAdminPagesAction {
+export function setAdminPages(adminPages: Extension[]) {
return { type: 'SET_ADMIN_PAGES', adminPages };
}
-export function requireAuthorization(): RequireAuthorizationAction {
+export function requireAuthorization() {
return { type: 'REQUIRE_AUTHORIZATION' };
}
const defaultValue: AppState = {
authenticationError: false,
authorizationError: false,
+ defaultOrganization: '',
+ edition: EditionKey.community,
organizationsEnabled: false,
- qualifiers: []
+ productionDatabase: true,
+ qualifiers: [],
+ version: ''
};
export default function(state: AppState = defaultValue, action: Action): AppState {
diff --git a/server/sonar-web/src/main/js/store/globalMessages.ts b/server/sonar-web/src/main/js/store/globalMessages.ts
new file mode 100644
index 00000000000..370ecb1bcc8
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/globalMessages.ts
@@ -0,0 +1,102 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { uniqueId } from 'lodash';
+import { Dispatch } from 'redux';
+import { requireAuthorization } from './appState';
+import { Store } from './rootReducer';
+import { ActionType } from './utils/actions';
+
+enum MessageLevel {
+ Error = 'ERROR',
+ Success = 'SUCCESS'
+}
+
+interface Message {
+ id: string;
+ message: string;
+ level: MessageLevel;
+}
+
+function addGlobalMessageActionCreator(id: string, message: string, level: MessageLevel) {
+ return { type: 'ADD_GLOBAL_MESSAGE', message, level, id };
+}
+
+export function closeGlobalMessage(id: string) {
+ return { type: 'CLOSE_GLOBAL_MESSAGE', id };
+}
+
+export function closeAllGlobalMessages(id: string) {
+ return { type: 'CLOSE_ALL_GLOBAL_MESSAGES', id };
+}
+
+type Action =
+ | ActionType<typeof addGlobalMessageActionCreator, 'ADD_GLOBAL_MESSAGE'>
+ | ActionType<typeof closeGlobalMessage, 'CLOSE_GLOBAL_MESSAGE'>
+ | ActionType<typeof closeAllGlobalMessages, 'CLOSE_ALL_GLOBAL_MESSAGES'>
+ | ActionType<typeof requireAuthorization, 'REQUIRE_AUTHORIZATION'>;
+
+function addGlobalMessage(message: string, level: MessageLevel) {
+ return (dispatch: Dispatch<Store>) => {
+ const id = uniqueId('global-message-');
+ dispatch(addGlobalMessageActionCreator(id, message, level));
+ setTimeout(() => dispatch(closeGlobalMessage(id)), 5000);
+ };
+}
+
+export function addGlobalErrorMessage(message: string) {
+ return addGlobalMessage(message, MessageLevel.Error);
+}
+
+export function addGlobalSuccessMessage(message: string) {
+ return addGlobalMessage(message, MessageLevel.Success);
+}
+
+export type State = Message[];
+
+export default function(state: State = [], action: Action): State {
+ switch (action.type) {
+ case 'ADD_GLOBAL_MESSAGE':
+ return [{ id: action.id, message: action.message, level: action.level }];
+
+ case 'REQUIRE_AUTHORIZATION':
+ // FIXME l10n
+ return [
+ {
+ id: uniqueId('global-message-'),
+ message:
+ 'You are not authorized to access this page. ' +
+ 'Please log in with more privileges and try again.',
+ level: MessageLevel.Error
+ }
+ ];
+
+ case 'CLOSE_GLOBAL_MESSAGE':
+ return state.filter(message => message.id !== action.id);
+
+ case 'CLOSE_ALL_GLOBAL_MESSAGES':
+ return [];
+ default:
+ return state;
+ }
+}
+
+export function getGlobalMessages(state: State) {
+ return state;
+}
diff --git a/server/sonar-web/src/main/js/store/globalMessages/duck.js b/server/sonar-web/src/main/js/store/globalMessages/duck.js
deleted file mode 100644
index 81c4cba0ff6..00000000000
--- a/server/sonar-web/src/main/js/store/globalMessages/duck.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-// @flow
-import { uniqueId } from 'lodash';
-
-/*::
-type Level = 'ERROR' | 'SUCCESS';
-*/
-
-/*::
-type Message = {
- id: string,
- message: string,
- level: Level
-};
-*/
-
-/*::
-export type State = Array<Message>;
-*/
-
-/*::
-type Action = Object;
-*/
-
-const ERROR = 'ERROR';
-const SUCCESS = 'SUCCESS';
-
-/* Actions */
-const ADD_GLOBAL_MESSAGE = 'ADD_GLOBAL_MESSAGE';
-const CLOSE_GLOBAL_MESSAGE = 'CLOSE_GLOBAL_MESSAGE';
-const CLOSE_ALL_GLOBAL_MESSAGES = 'CLOSE_ALL_GLOBAL_MESSAGES';
-
-function addGlobalMessageActionCreator(
- id /*: string */,
- message /*: string */,
- level /*: Level */
-) {
- return {
- type: ADD_GLOBAL_MESSAGE,
- message,
- level,
- id
- };
-}
-
-export function closeGlobalMessage(id /*: string */) {
- return {
- type: CLOSE_GLOBAL_MESSAGE,
- id
- };
-}
-
-export function closeAllGlobalMessages(id /*: string */) {
- return {
- type: CLOSE_ALL_GLOBAL_MESSAGES,
- id
- };
-}
-
-function addGlobalMessage(message /*: string */, level /*: Level */) {
- return function(dispatch /*: Function */) {
- const id = uniqueId('global-message-');
- dispatch(addGlobalMessageActionCreator(id, message, level));
- setTimeout(() => dispatch(closeGlobalMessage(id)), 5000);
- };
-}
-
-export function addGlobalErrorMessage(message /*: string */) {
- return addGlobalMessage(message, ERROR);
-}
-
-export function addGlobalSuccessMessage(message /*: string */) {
- return addGlobalMessage(message, SUCCESS);
-}
-
-/* Reducer */
-export default function(state /*: State */ = [], action /*: Action */ = {}) {
- switch (action.type) {
- case ADD_GLOBAL_MESSAGE:
- return [
- {
- id: action.id,
- message: action.message,
- level: action.level
- }
- ];
-
- case 'REQUIRE_AUTHORIZATION':
- // FIXME l10n
- return [
- {
- id: uniqueId('global-message-'),
- message:
- 'You are not authorized to access this page. ' +
- 'Please log in with more privileges and try again.',
- level: ERROR
- }
- ];
-
- case CLOSE_GLOBAL_MESSAGE:
- return state.filter(message => message.id !== action.id);
-
- case CLOSE_ALL_GLOBAL_MESSAGES:
- return [];
- default:
- return state;
- }
-}
-
-/* Selectors */
-export function getGlobalMessages(state /*: State */) {
- return state;
-}
diff --git a/server/sonar-web/src/main/js/store/languages/reducer.ts b/server/sonar-web/src/main/js/store/languages.ts
index 50caf69c59b..2cc95fe3006 100644
--- a/server/sonar-web/src/main/js/store/languages/reducer.ts
+++ b/server/sonar-web/src/main/js/store/languages.ts
@@ -18,22 +18,30 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { keyBy } from 'lodash';
-import { RECEIVE_LANGUAGES } from './actions';
+import { ActionType } from './utils/actions';
+
+export function receiveLanguages(languages: Array<{ key: string; name: string }>) {
+ return { type: 'RECEIVE_LANGUAGES', languages };
+}
+
+type Action = ActionType<typeof receiveLanguages, 'RECEIVE_LANGUAGES'>;
export interface Languages {
[key: string]: { key: string; name: string };
}
-const reducer = (state: Languages = {}, action: any = {}) => {
- if (action.type === RECEIVE_LANGUAGES) {
+export default function(state: Languages = {}, action: Action): Languages {
+ if (action.type === 'RECEIVE_LANGUAGES') {
return keyBy(action.languages, 'key');
}
return state;
-};
-
-export default reducer;
+}
-export const getLanguages = (state: Languages) => state;
+export function getLanguages(state: Languages) {
+ return state;
+}
-export const getLanguageByKey = (state: Languages, key: string) => state[key];
+export function getLanguageByKey(state: Languages, key: string) {
+ return state[key];
+}
diff --git a/server/sonar-web/src/main/js/store/metrics/reducer.js b/server/sonar-web/src/main/js/store/metrics.ts
index d2d5284cc3a..85098902b21 100644
--- a/server/sonar-web/src/main/js/store/metrics/reducer.js
+++ b/server/sonar-web/src/main/js/store/metrics.ts
@@ -17,27 +17,28 @@
* 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 { keyBy } from 'lodash';
-import { RECEIVE_METRICS } from './actions';
-/*:: import type { Metric } from './actions'; */
+import { combineReducers } from 'redux';
+import { ActionType } from './utils/actions';
+import { Metric } from '../app/types';
+
+export function receiveMetrics(metrics: Metric[]) {
+ return { type: 'RECEIVE_METRICS', metrics };
+}
-/*::
-type StateByKey = { [string]: Metric };
-type StateKeys = Array<string>;
-type State = { byKey: StateByKey, keys: StateKeys };
-*/
+type Action = ActionType<typeof receiveMetrics, 'RECEIVE_METRICS'>;
-const byKey = (state /*: StateByKey */ = {}, action = {}) => {
- if (action.type === RECEIVE_METRICS) {
+export type State = { byKey: { [key: string]: Metric }; keys: string[] };
+
+const byKey = (state: State['byKey'] = {}, action: Action) => {
+ if (action.type === 'RECEIVE_METRICS') {
return keyBy(action.metrics, 'key');
}
return state;
};
-const keys = (state /*: StateKeys */ = [], action = {}) => {
- if (action.type === RECEIVE_METRICS) {
+const keys = (state: State['keys'] = [], action: Action) => {
+ if (action.type === 'RECEIVE_METRICS') {
return action.metrics.map(f => f.key);
}
@@ -46,5 +47,10 @@ const keys = (state /*: StateKeys */ = [], action = {}) => {
export default combineReducers({ byKey, keys });
-export const getMetrics = (state /*: State */) => state.byKey;
-export const getMetricsKey = (state /*: State */) => state.keys;
+export function getMetrics(state: State) {
+ return state.byKey;
+}
+
+export function getMetricsKey(state: State) {
+ return state.keys;
+}
diff --git a/server/sonar-web/src/main/js/store/metrics/actions.js b/server/sonar-web/src/main/js/store/metrics/actions.js
deleted file mode 100644
index a271928ce3b..00000000000
--- a/server/sonar-web/src/main/js/store/metrics/actions.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-// @flow
-/*:: export type Metric = {
- bestValue?: string,
- custom?: boolean,
- decimalScale?: number,
- description?: string,
- direction?: number,
- domain?: string,
- hidden?: boolean,
- key: string,
- name: string,
- qualitative?: boolean,
- type: string
-}; */
-
-export const RECEIVE_METRICS = 'RECEIVE_METRICS';
-
-export const receiveMetrics = (metrics /*: Array<Metric> */) => ({
- type: RECEIVE_METRICS,
- metrics
-});
diff --git a/server/sonar-web/src/main/js/store/organizations.ts b/server/sonar-web/src/main/js/store/organizations.ts
new file mode 100644
index 00000000000..6b1dfdcc28c
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/organizations.ts
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { combineReducers } from 'redux';
+import { omit, uniq, without } from 'lodash';
+import { ActionType } from './utils/actions';
+import { Organization, OrganizationBase } from '../app/types';
+
+type ReceiveOrganizationsAction =
+ | ActionType<typeof receiveOrganizations, 'RECEIVE_ORGANIZATIONS'>
+ | ActionType<typeof receiveMyOrganizations, 'RECEIVE_MY_ORGANIZATIONS'>;
+
+type Action =
+ | ReceiveOrganizationsAction
+ | ActionType<typeof createOrganization, 'CREATE_ORGANIZATION'>
+ | ActionType<typeof updateOrganization, 'UPDATE_ORGANIZATION'>
+ | ActionType<typeof deleteOrganization, 'DELETE_ORGANIZATION'>;
+
+export interface State {
+ byKey: { [key: string]: Organization };
+ my: string[];
+}
+
+export function receiveOrganizations(organizations: Organization[]) {
+ return { type: 'RECEIVE_ORGANIZATIONS', organizations };
+}
+
+export function receiveMyOrganizations(organizations: Organization[]) {
+ return { type: 'RECEIVE_MY_ORGANIZATIONS', organizations };
+}
+
+export function createOrganization(organization: Organization) {
+ return { type: 'CREATE_ORGANIZATION', organization };
+}
+
+export function updateOrganization(key: string, changes: OrganizationBase) {
+ return { type: 'UPDATE_ORGANIZATION', key, changes };
+}
+
+export function deleteOrganization(key: string) {
+ return { type: 'DELETE_ORGANIZATION', key };
+}
+
+function onReceiveOrganizations(state: State['byKey'], action: ReceiveOrganizationsAction) {
+ const nextState = { ...state };
+ action.organizations.forEach(organization => {
+ nextState[organization.key] = { ...state[organization.key], ...organization };
+ });
+ return nextState;
+}
+
+function byKey(state: State['byKey'] = {}, action: Action): State['byKey'] {
+ switch (action.type) {
+ case 'RECEIVE_ORGANIZATIONS':
+ case 'RECEIVE_MY_ORGANIZATIONS':
+ return onReceiveOrganizations(state, action);
+ case 'CREATE_ORGANIZATION':
+ return { ...state, [action.organization.key]: { ...action.organization, isAdmin: true } };
+ case 'UPDATE_ORGANIZATION':
+ return {
+ ...state,
+ [action.key]: {
+ ...state[action.key],
+ key: action.key,
+ ...action.changes
+ }
+ };
+ case 'DELETE_ORGANIZATION':
+ return omit(state, action.key);
+ default:
+ return state;
+ }
+}
+
+function my(state: State['my'] = [], action: Action): State['my'] {
+ switch (action.type) {
+ case 'RECEIVE_MY_ORGANIZATIONS':
+ return uniq([...state, ...action.organizations.map(o => o.key)]);
+ case 'CREATE_ORGANIZATION':
+ return uniq([...state, action.organization.key]);
+ case 'DELETE_ORGANIZATION':
+ return without(state, action.key);
+ default:
+ return state;
+ }
+}
+
+export default combineReducers({ byKey, my });
+
+export function getOrganizationByKey(state: State, key: string) {
+ return state.byKey[key];
+}
+
+export function getMyOrganizations(state: State) {
+ return state.my.map(key => getOrganizationByKey(state, key));
+}
+
+export function areThereCustomOrganizations(state: State) {
+ return Object.keys(state.byKey).length > 1;
+}
diff --git a/server/sonar-web/src/main/js/store/organizations/__tests__/__snapshots__/duck-test.ts.snap b/server/sonar-web/src/main/js/store/organizations/__tests__/__snapshots__/duck-test.ts.snap
deleted file mode 100644
index c5ad9b73625..00000000000
--- a/server/sonar-web/src/main/js/store/organizations/__tests__/__snapshots__/duck-test.ts.snap
+++ /dev/null
@@ -1,43 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Reducer should have initial state 1`] = `
-Object {
- "byKey": Object {},
- "groups": Object {},
- "my": Array [],
-}
-`;
-
-exports[`Reducer should receive organizations 1`] = `
-Object {
- "byKey": Object {
- "bar": Object {
- "key": "bar",
- "name": "Bar",
- },
- "foo": Object {
- "key": "foo",
- "name": "Foo",
- },
- },
- "groups": Object {},
- "my": Array [],
-}
-`;
-
-exports[`Reducer should receive organizations 2`] = `
-Object {
- "byKey": Object {
- "bar": Object {
- "key": "bar",
- "name": "Bar",
- },
- "foo": Object {
- "key": "foo",
- "name": "Qwe",
- },
- },
- "groups": Object {},
- "my": Array [],
-}
-`;
diff --git a/server/sonar-web/src/main/js/store/organizations/__tests__/duck-test.ts b/server/sonar-web/src/main/js/store/organizations/__tests__/duck-test.ts
deleted file mode 100644
index e9321e29f90..00000000000
--- a/server/sonar-web/src/main/js/store/organizations/__tests__/duck-test.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import organizations, { getOrganizationByKey, areThereCustomOrganizations } from '../duck';
-
-const state0 = { byKey: {}, my: [], groups: {} };
-
-describe('Reducer', () => {
- it('should have initial state', () => {
- expect((organizations as any)(undefined, {})).toMatchSnapshot();
- });
-
- it('should receive organizations', () => {
- const action1 = {
- type: 'RECEIVE_ORGANIZATIONS',
- organizations: [{ key: 'foo', name: 'Foo' }, { key: 'bar', name: 'Bar' }]
- };
- const state1 = organizations(state0, action1);
- expect(state1).toMatchSnapshot();
-
- const action2 = {
- type: 'RECEIVE_ORGANIZATIONS',
- organizations: [{ key: 'foo', name: 'Qwe' }]
- };
- const state2 = organizations(state1, action2);
- expect(state2).toMatchSnapshot();
- });
-});
-
-describe('Selectors', () => {
- it('getOrganizationByKey', () => {
- const foo = { key: 'foo', name: 'Foo' };
- const state = { ...state0, byKey: { foo } };
- expect(getOrganizationByKey(state, 'foo')).toBe(foo);
- expect(getOrganizationByKey(state, 'bar')).toBeFalsy();
- });
-
- it('areThereCustomOrganizations', () => {
- const foo = { key: 'foo', name: 'Foo' };
- const bar = { key: 'bar', name: 'Bar' };
- expect(areThereCustomOrganizations({ ...state0, byKey: {} })).toBe(false);
- expect(areThereCustomOrganizations({ ...state0, byKey: { foo } })).toBe(false);
- expect(areThereCustomOrganizations({ ...state0, byKey: { foo, bar } })).toBe(true);
- });
-});
diff --git a/server/sonar-web/src/main/js/store/organizations/duck.ts b/server/sonar-web/src/main/js/store/organizations/duck.ts
deleted file mode 100644
index 5f01ac95261..00000000000
--- a/server/sonar-web/src/main/js/store/organizations/duck.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { combineReducers } from 'redux';
-import { omit, uniq, without } from 'lodash';
-import { Group, Organization, OrganizationBase } from '../../app/types';
-
-interface ReceiveOrganizationsAction {
- type: 'RECEIVE_ORGANIZATIONS';
- organizations: Organization[];
-}
-
-interface ReceiveMyOrganizationsAction {
- type: 'RECEIVE_MY_ORGANIZATIONS';
- organizations: Organization[];
-}
-
-interface ReceiveOrganizationGroups {
- type: 'RECEIVE_ORGANIZATION_GROUPS';
- key: string;
- groups: Group[];
-}
-
-interface CreateOrganizationAction {
- type: 'CREATE_ORGANIZATION';
- organization: Organization;
-}
-
-interface UpdateOrganizationAction {
- type: 'UPDATE_ORGANIZATION';
- key: string;
- changes: {};
-}
-
-interface DeleteOrganizationAction {
- type: 'DELETE_ORGANIZATION';
- key: string;
-}
-
-type Action =
- | ReceiveOrganizationsAction
- | ReceiveMyOrganizationsAction
- | ReceiveOrganizationGroups
- | CreateOrganizationAction
- | UpdateOrganizationAction
- | DeleteOrganizationAction;
-
-interface ByKeyState {
- [key: string]: Organization;
-}
-
-interface GroupsState {
- [key: string]: Group[];
-}
-
-type MyState = string[];
-
-interface State {
- byKey: ByKeyState;
- my: MyState;
- groups: GroupsState;
-}
-
-export function receiveOrganizations(organizations: Organization[]): ReceiveOrganizationsAction {
- return {
- type: 'RECEIVE_ORGANIZATIONS',
- organizations
- };
-}
-
-export function receiveMyOrganizations(
- organizations: Organization[]
-): ReceiveMyOrganizationsAction {
- return {
- type: 'RECEIVE_MY_ORGANIZATIONS',
- organizations
- };
-}
-
-export function receiveOrganizationGroups(key: string, groups: Group[]): ReceiveOrganizationGroups {
- return {
- type: 'RECEIVE_ORGANIZATION_GROUPS',
- key,
- groups
- };
-}
-
-export function createOrganization(organization: Organization): CreateOrganizationAction {
- return {
- type: 'CREATE_ORGANIZATION',
- organization
- };
-}
-
-export function updateOrganization(
- key: string,
- changes: OrganizationBase
-): UpdateOrganizationAction {
- return {
- type: 'UPDATE_ORGANIZATION',
- key,
- changes
- };
-}
-
-export function deleteOrganization(key: string): DeleteOrganizationAction {
- return {
- type: 'DELETE_ORGANIZATION',
- key
- };
-}
-
-function onReceiveOrganizations(
- state: ByKeyState,
- action: ReceiveOrganizationsAction | ReceiveMyOrganizationsAction
-): ByKeyState {
- const nextState = { ...state };
- action.organizations.forEach(organization => {
- nextState[organization.key] = { ...state[organization.key], ...organization };
- });
- return nextState;
-}
-
-function byKey(state: ByKeyState = {}, action: Action) {
- switch (action.type) {
- case 'RECEIVE_ORGANIZATIONS':
- case 'RECEIVE_MY_ORGANIZATIONS':
- return onReceiveOrganizations(state, action);
- case 'CREATE_ORGANIZATION':
- return { ...state, [action.organization.key]: { ...action.organization, isAdmin: true } };
- case 'UPDATE_ORGANIZATION':
- return {
- ...state,
- [action.key]: {
- ...state[action.key],
- key: action.key,
- ...action.changes
- }
- };
- case 'DELETE_ORGANIZATION':
- return omit(state, action.key);
- default:
- return state;
- }
-}
-
-function my(state: MyState = [], action: Action) {
- switch (action.type) {
- case 'RECEIVE_MY_ORGANIZATIONS':
- return uniq([...state, ...action.organizations.map(o => o.key)]);
- case 'CREATE_ORGANIZATION':
- return uniq([...state, action.organization.key]);
- case 'DELETE_ORGANIZATION':
- return without(state, action.key);
- default:
- return state;
- }
-}
-
-function groups(state: GroupsState = {}, action: Action) {
- if (action.type === 'RECEIVE_ORGANIZATION_GROUPS') {
- return { ...state, [action.key]: action.groups };
- }
- return state;
-}
-
-export default combineReducers<State>({ byKey, my, groups });
-
-export function getOrganizationByKey(state: State, key: string): Organization | undefined {
- return state.byKey[key];
-}
-
-export function getOrganizationGroupsByKey(state: State, key: string): Group[] {
- return state.groups[key] || [];
-}
-
-export function getMyOrganizations(state: State): Organization[] {
- return state.my.map(key => getOrganizationByKey(state, key) as Organization);
-}
-
-export function areThereCustomOrganizations(state: State): boolean {
- return Object.keys(state.byKey).length > 1;
-}
diff --git a/server/sonar-web/src/main/js/store/rootActions.js b/server/sonar-web/src/main/js/store/rootActions.js
deleted file mode 100644
index c49e3921c6c..00000000000
--- a/server/sonar-web/src/main/js/store/rootActions.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { setAppState } from './appState/duck';
-import { receiveOrganizations } from './organizations/duck';
-import { receiveLanguages } from './languages/actions';
-import { receiveMetrics } from './metrics/actions';
-import { addGlobalErrorMessage } from './globalMessages/duck';
-import { getLanguages } from '../api/languages';
-import { getGlobalNavigation } from '../api/nav';
-import * as auth from '../api/auth';
-import { getOrganizations } from '../api/organizations';
-import { getAllMetrics } from '../api/metrics';
-import { parseError } from '../helpers/request';
-
-export const onFail = dispatch => error =>
- parseError(error).then(message => dispatch(addGlobalErrorMessage(message)));
-
-export const fetchLanguages = () => dispatch =>
- getLanguages().then(languages => dispatch(receiveLanguages(languages)), onFail(dispatch));
-
-export const fetchMetrics = () => dispatch =>
- getAllMetrics().then(metrics => dispatch(receiveMetrics(metrics)), onFail(dispatch));
-
-export const fetchOrganizations = (organizations /*: Array<string> | void */) => dispatch =>
- getOrganizations({ organizations: organizations && organizations.join() }).then(
- r => dispatch(receiveOrganizations(r.organizations)),
- onFail(dispatch)
- );
-
-export const doLogin = (login, password) => dispatch =>
- auth.login(login, password).then(
- () => {
- /* everything is fine */
- },
- () => {
- dispatch(addGlobalErrorMessage('Authentication failed'));
- return Promise.reject();
- }
- );
-
-export const doLogout = () => dispatch =>
- auth.logout().then(
- () => {
- /* everything is fine */
- },
- () => {
- dispatch(addGlobalErrorMessage('Logout failed'));
- return Promise.reject();
- }
- );
diff --git a/server/sonar-web/src/main/js/store/rootActions.ts b/server/sonar-web/src/main/js/store/rootActions.ts
new file mode 100644
index 00000000000..52ab8e776b6
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/rootActions.ts
@@ -0,0 +1,76 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { Dispatch } from 'redux';
+import { addGlobalErrorMessage } from './globalMessages';
+import { receiveLanguages } from './languages';
+import { receiveMetrics } from './metrics';
+import { receiveOrganizations } from './organizations';
+import { Store } from './rootReducer';
+import * as auth from '../api/auth';
+import { getLanguages } from '../api/languages';
+import { getAllMetrics } from '../api/metrics';
+import { getOrganizations } from '../api/organizations';
+
+export function fetchLanguages() {
+ return (dispatch: Dispatch<Store>) => {
+ getLanguages().then(languages => dispatch(receiveLanguages(languages)), () => {});
+ };
+}
+
+export function fetchMetrics() {
+ return (dispatch: Dispatch<Store>) => {
+ getAllMetrics().then(metrics => dispatch(receiveMetrics(metrics)), () => {});
+ };
+}
+
+export function fetchOrganizations(organizations: string[]) {
+ return (dispatch: Dispatch<Store>) => {
+ getOrganizations({ organizations: organizations && organizations.join() }).then(
+ r => dispatch(receiveOrganizations(r.organizations)),
+ () => {}
+ );
+ };
+}
+
+export function doLogin(login: string, password: string) {
+ return (dispatch: Dispatch<Store>) =>
+ auth.login(login, password).then(
+ () => {
+ /* everything is fine */
+ },
+ () => {
+ dispatch(addGlobalErrorMessage('Authentication failed'));
+ return Promise.reject();
+ }
+ );
+}
+
+export function doLogout() {
+ return (dispatch: Dispatch<Store>) =>
+ auth.logout().then(
+ () => {
+ /* everything is fine */
+ },
+ () => {
+ dispatch(addGlobalErrorMessage('Logout failed'));
+ return Promise.reject();
+ }
+ );
+}
diff --git a/server/sonar-web/src/main/js/store/rootReducer.js b/server/sonar-web/src/main/js/store/rootReducer.js
deleted file mode 100644
index 16d038d1e89..00000000000
--- a/server/sonar-web/src/main/js/store/rootReducer.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { combineReducers } from 'redux';
-import appState from './appState/duck';
-import users, * as fromUsers from './users/reducer';
-import languages, * as fromLanguages from './languages/reducer';
-import metrics, * as fromMetrics from './metrics/reducer';
-import organizations, * as fromOrganizations from './organizations/duck';
-import globalMessages, * as fromGlobalMessages from './globalMessages/duck';
-import permissionsApp, * as fromPermissionsApp from '../apps/permissions/shared/store/rootReducer';
-import projectAdminApp, * as fromProjectAdminApp from '../apps/project-admin/store/rootReducer';
-import settingsApp, * as fromSettingsApp from '../apps/settings/store/rootReducer';
-
-export default combineReducers({
- appState,
- globalMessages,
- languages,
- metrics,
- organizations,
- users,
-
- // apps
- permissionsApp,
- projectAdminApp,
- settingsApp
-});
-
-export const getAppState = state => state.appState;
-
-export const getGlobalMessages = state =>
- fromGlobalMessages.getGlobalMessages(state.globalMessages);
-
-export const getLanguages = state => fromLanguages.getLanguages(state.languages);
-
-export const getCurrentUser = state => fromUsers.getCurrentUser(state.users);
-
-export const getUsersByLogins = (state, logins) => fromUsers.getUsersByLogins(state.users, logins);
-
-export const getMetrics = state => fromMetrics.getMetrics(state.metrics);
-
-export const getMetricsKey = state => fromMetrics.getMetricsKey(state.metrics);
-
-export const getOrganizationByKey = (state, key) =>
- fromOrganizations.getOrganizationByKey(state.organizations, key);
-
-export const getOrganizationGroupsByKey = (state, key) =>
- fromOrganizations.getOrganizationGroupsByKey(state.organizations, key);
-
-export const getMyOrganizations = state =>
- fromOrganizations.getMyOrganizations(state.organizations);
-
-export const areThereCustomOrganizations = state => getAppState(state).organizationsEnabled;
-
-export const getPermissionsAppUsers = state => fromPermissionsApp.getUsers(state.permissionsApp);
-
-export const getPermissionsAppGroups = state => fromPermissionsApp.getGroups(state.permissionsApp);
-
-export const isPermissionsAppLoading = state => fromPermissionsApp.isLoading(state.permissionsApp);
-
-export const getPermissionsAppQuery = state => fromPermissionsApp.getQuery(state.permissionsApp);
-
-export const getPermissionsAppFilter = state => fromPermissionsApp.getFilter(state.permissionsApp);
-
-export const getPermissionsAppSelectedPermission = state =>
- fromPermissionsApp.getSelectedPermission(state.permissionsApp);
-
-export const getPermissionsAppError = state => fromPermissionsApp.getError(state.permissionsApp);
-
-export const getGlobalSettingValue = (state, key) =>
- fromSettingsApp.getValue(state.settingsApp, key);
-
-export const getSettingsAppDefinition = (state, key) =>
- fromSettingsApp.getDefinition(state.settingsApp, key);
-
-export const getSettingsAppAllCategories = state =>
- fromSettingsApp.getAllCategories(state.settingsApp);
-
-export const getSettingsAppDefaultCategory = state =>
- fromSettingsApp.getDefaultCategory(state.settingsApp);
-
-export const getSettingsAppSettingsForCategory = (state, category, componentKey) =>
- fromSettingsApp.getSettingsForCategory(state.settingsApp, category, componentKey);
-
-export const getSettingsAppChangedValue = (state, key) =>
- fromSettingsApp.getChangedValue(state.settingsApp, key);
-
-export const isSettingsAppLoading = (state, key) =>
- fromSettingsApp.isLoading(state.settingsApp, key);
-
-export const getSettingsAppValidationMessage = (state, key) =>
- fromSettingsApp.getValidationMessage(state.settingsApp, key);
-
-export const getSettingsAppEncryptionState = state =>
- fromSettingsApp.getEncryptionState(state.settingsApp);
-
-export const getProjectAdminProjectModules = (state, projectKey) =>
- fromProjectAdminApp.getProjectModules(state.projectAdminApp, projectKey);
diff --git a/server/sonar-web/src/main/js/store/rootReducer.ts b/server/sonar-web/src/main/js/store/rootReducer.ts
new file mode 100644
index 00000000000..8dfc49d857f
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/rootReducer.ts
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { combineReducers } from 'redux';
+import appState from './appState';
+import globalMessages, * as fromGlobalMessages from './globalMessages';
+import languages, * as fromLanguages from './languages';
+import metrics, * as fromMetrics from './metrics';
+import organizations, * as fromOrganizations from './organizations';
+import users, * as fromUsers from './users';
+import { AppState } from '../app/types';
+import permissionsApp, * as fromPermissionsApp from '../apps/permissions/shared/store/rootReducer';
+import projectAdminApp, * as fromProjectAdminApp from '../apps/project-admin/store/rootReducer';
+import settingsApp, * as fromSettingsApp from '../apps/settings/store/rootReducer';
+
+export type Store = {
+ appState: AppState;
+ globalMessages: fromGlobalMessages.State;
+ languages: fromLanguages.Languages;
+ metrics: fromMetrics.State;
+ organizations: fromOrganizations.State;
+ users: fromUsers.State;
+
+ // apps
+ permissionsApp: any;
+ projectAdminApp: any;
+ settingsApp: any;
+};
+
+export default combineReducers<Store>({
+ appState,
+ globalMessages,
+ languages,
+ metrics,
+ organizations,
+ users,
+
+ // apps
+ permissionsApp,
+ projectAdminApp,
+ settingsApp
+});
+
+export function getAppState(state: Store) {
+ return state.appState;
+}
+
+export function getGlobalMessages(state: Store) {
+ return fromGlobalMessages.getGlobalMessages(state.globalMessages);
+}
+
+export function getLanguages(state: Store) {
+ return fromLanguages.getLanguages(state.languages);
+}
+
+export function getCurrentUser(state: Store) {
+ return fromUsers.getCurrentUser(state.users);
+}
+
+export function getMetrics(state: Store) {
+ return fromMetrics.getMetrics(state.metrics);
+}
+
+export function getMetricsKey(state: Store) {
+ return fromMetrics.getMetricsKey(state.metrics);
+}
+
+export function getOrganizationByKey(state: Store, key: string) {
+ return fromOrganizations.getOrganizationByKey(state.organizations, key);
+}
+
+export function getMyOrganizations(state: Store) {
+ return fromOrganizations.getMyOrganizations(state.organizations);
+}
+
+export function areThereCustomOrganizations(state: Store) {
+ return getAppState(state).organizationsEnabled;
+}
+
+export function getPermissionsAppUsers(state: Store) {
+ return fromPermissionsApp.getUsers(state.permissionsApp);
+}
+
+export function getPermissionsAppGroups(state: Store) {
+ return fromPermissionsApp.getGroups(state.permissionsApp);
+}
+
+export function isPermissionsAppLoading(state: Store) {
+ return fromPermissionsApp.isLoading(state.permissionsApp);
+}
+
+export function getPermissionsAppQuery(state: Store) {
+ return fromPermissionsApp.getQuery(state.permissionsApp);
+}
+
+export function getPermissionsAppFilter(state: Store) {
+ return fromPermissionsApp.getFilter(state.permissionsApp);
+}
+
+export function getPermissionsAppSelectedPermission(state: Store) {
+ return fromPermissionsApp.getSelectedPermission(state.permissionsApp);
+}
+
+export function getPermissionsAppError(state: Store) {
+ return fromPermissionsApp.getError(state.permissionsApp);
+}
+
+export function getGlobalSettingValue(state: Store, key: string) {
+ return fromSettingsApp.getValue(state.settingsApp, key);
+}
+
+export function getSettingsAppDefinition(state: Store, key: string) {
+ return fromSettingsApp.getDefinition(state.settingsApp, key);
+}
+
+export function getSettingsAppAllCategories(state: Store) {
+ return fromSettingsApp.getAllCategories(state.settingsApp);
+}
+
+export function getSettingsAppDefaultCategory(state: Store) {
+ return fromSettingsApp.getDefaultCategory(state.settingsApp);
+}
+
+export function getSettingsAppSettingsForCategory(
+ state: Store,
+ category: string,
+ componentKey: string
+) {
+ return fromSettingsApp.getSettingsForCategory(state.settingsApp, category, componentKey);
+}
+
+export function getSettingsAppChangedValue(state: Store, key: string) {
+ return fromSettingsApp.getChangedValue(state.settingsApp, key);
+}
+
+export function isSettingsAppLoading(state: Store, key: string) {
+ return fromSettingsApp.isLoading(state.settingsApp, key);
+}
+
+export function getSettingsAppValidationMessage(state: Store, key: string) {
+ return fromSettingsApp.getValidationMessage(state.settingsApp, key);
+}
+
+export function getSettingsAppEncryptionState(state: Store) {
+ return fromSettingsApp.getEncryptionState(state.settingsApp);
+}
+
+export function getProjectAdminProjectModules(state: Store, projectKey: string) {
+ return fromProjectAdminApp.getProjectModules(state.projectAdminApp, projectKey);
+}
diff --git a/server/sonar-web/src/main/js/store/users.ts b/server/sonar-web/src/main/js/store/users.ts
new file mode 100644
index 00000000000..969cf8a6f7e
--- /dev/null
+++ b/server/sonar-web/src/main/js/store/users.ts
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { uniq } from 'lodash';
+import { Dispatch, combineReducers } from 'redux';
+import { Store } from './rootReducer';
+import { ActionType } from './utils/actions';
+import * as api from '../api/users';
+import { CurrentUser, HomePage, isLoggedIn, LoggedInUser } from '../app/types';
+
+export function receiveCurrentUser(user: CurrentUser) {
+ return { type: 'RECEIVE_CURRENT_USER', user };
+}
+
+export function skipOnboarding() {
+ return { type: 'SKIP_ONBOARDING' };
+}
+
+function setHomePageAction(homepage: HomePage) {
+ return { type: 'SET_HOMEPAGE', homepage };
+}
+
+export function setHomePage(homepage: HomePage) {
+ return (dispatch: Dispatch<Store>) => {
+ api.setHomePage(homepage).then(
+ () => {
+ dispatch(setHomePageAction(homepage));
+ },
+ () => {}
+ );
+ };
+}
+
+type Action =
+ | ActionType<typeof receiveCurrentUser, 'RECEIVE_CURRENT_USER'>
+ | ActionType<typeof skipOnboarding, 'SKIP_ONBOARDING'>
+ | ActionType<typeof setHomePageAction, 'SET_HOMEPAGE'>;
+
+export interface State {
+ usersByLogin: { [login: string]: any };
+ userLogins: string[];
+ currentUser: CurrentUser;
+}
+
+function usersByLogin(state: State['usersByLogin'] = {}, action: Action): State['usersByLogin'] {
+ if (action.type === 'RECEIVE_CURRENT_USER' && isLoggedIn(action.user)) {
+ return { ...state, [action.user.login]: action.user };
+ } else {
+ return state;
+ }
+}
+
+function userLogins(state: State['userLogins'] = [], action: Action): State['userLogins'] {
+ if (action.type === 'RECEIVE_CURRENT_USER' && isLoggedIn(action.user)) {
+ return uniq([...state, action.user.login]);
+ } else {
+ return state;
+ }
+}
+
+function currentUser(
+ state: State['currentUser'] = { isLoggedIn: false },
+ action: Action
+): State['currentUser'] {
+ if (action.type === 'RECEIVE_CURRENT_USER') {
+ return action.user;
+ }
+ if (action.type === 'SKIP_ONBOARDING' && isLoggedIn(state)) {
+ return { ...state, showOnboardingTutorial: false } as LoggedInUser;
+ }
+ if (action.type === 'SET_HOMEPAGE' && isLoggedIn(state)) {
+ return { ...state, homepage: action.homepage } as LoggedInUser;
+ }
+ return state;
+}
+
+export default combineReducers({ usersByLogin, userLogins, currentUser });
+
+export function getCurrentUser(state: State) {
+ return state.currentUser;
+}
+
+export function getUserByLogin(state: State, login: string) {
+ return state.usersByLogin[login];
+}
+
+export function getUsersByLogins(state: State, logins: string[]) {
+ return logins.map(login => getUserByLogin(state, login));
+}
diff --git a/server/sonar-web/src/main/js/store/users/actions.ts b/server/sonar-web/src/main/js/store/users/actions.ts
deleted file mode 100644
index 9c9a146e2a4..00000000000
--- a/server/sonar-web/src/main/js/store/users/actions.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { Dispatch } from 'redux';
-import * as api from '../../api/users';
-import { CurrentUser, HomePage } from '../../app/types';
-
-export const RECEIVE_CURRENT_USER = 'RECEIVE_CURRENT_USER';
-export const SKIP_ONBOARDING = 'SKIP_ONBOARDING';
-export const SET_HOMEPAGE = 'SET_HOMEPAGE';
-
-export const receiveCurrentUser = (user: CurrentUser) => ({
- type: RECEIVE_CURRENT_USER,
- user
-});
-
-export const skipOnboarding = () => ({ type: SKIP_ONBOARDING });
-
-export const fetchCurrentUser = () => (dispatch: Dispatch<any>) => {
- return api.getCurrentUser().then(user => {
- dispatch(receiveCurrentUser(user));
- return user;
- });
-};
-
-export const setHomePage = (homepage: HomePage) => (dispatch: Dispatch<any>) => {
- api.setHomePage(homepage).then(
- () => {
- dispatch({ type: SET_HOMEPAGE, homepage });
- },
- () => {}
- );
-};
diff --git a/server/sonar-web/src/main/js/store/users/reducer.ts b/server/sonar-web/src/main/js/store/users/reducer.ts
deleted file mode 100644
index a6e79db6d97..00000000000
--- a/server/sonar-web/src/main/js/store/users/reducer.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { combineReducers } from 'redux';
-import { uniq } from 'lodash';
-import { RECEIVE_CURRENT_USER, SKIP_ONBOARDING, SET_HOMEPAGE } from './actions';
-import { CurrentUser } from '../../app/types';
-
-interface UsersByLogin {
- [login: string]: any;
-}
-
-const usersByLogin = (state: UsersByLogin = {}, action: any = {}) => {
- if (action.type === RECEIVE_CURRENT_USER) {
- return { ...state, [action.user.login]: action.user };
- } else {
- return state;
- }
-};
-
-type UserLogins = string[];
-
-const userLogins = (state: UserLogins = [], action: any = {}) => {
- if (action.type === RECEIVE_CURRENT_USER) {
- return uniq([...state, action.user.login]);
- } else {
- return state;
- }
-};
-
-const currentUser = (state: CurrentUser | null = null, action: any = {}) => {
- if (action.type === RECEIVE_CURRENT_USER) {
- return action.user;
- }
- if (action.type === SKIP_ONBOARDING) {
- return state ? { ...state, showOnboardingTutorial: false } : null;
- }
- if (action.type === SET_HOMEPAGE) {
- return state && { ...state, homepage: action.homepage };
- }
- return state;
-};
-
-interface State {
- usersByLogin: UsersByLogin;
- userLogins: UserLogins;
- currentUser: CurrentUser | null;
-}
-
-export default combineReducers({ usersByLogin, userLogins, currentUser });
-
-export const getCurrentUser = (state: State) => state.currentUser!;
-export const getUserByLogin = (state: State, login: string) => state.usersByLogin[login];
-export const getUsersByLogins = (state: State, logins: string[]) =>
- logins.map(login => getUserByLogin(state, login));
diff --git a/server/sonar-web/src/main/js/store/languages/actions.js b/server/sonar-web/src/main/js/store/utils/actions.ts
index 9b3d1a9d506..4926a67d2a3 100644
--- a/server/sonar-web/src/main/js/store/languages/actions.js
+++ b/server/sonar-web/src/main/js/store/utils/actions.ts
@@ -17,9 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-export const RECEIVE_LANGUAGES = 'RECEIVE_LANGUAGES';
+import { Omit } from '../../app/types';
-export const receiveLanguages = languages => ({
- type: RECEIVE_LANGUAGES,
- languages
-});
+type ActionCreator = (...args: any[]) => { type: string };
+
+export type ActionType<F extends ActionCreator, T> = Omit<ReturnType<F>, 'type'> & { type: T };
diff --git a/server/sonar-web/src/main/js/store/utils/configureStore.js b/server/sonar-web/src/main/js/store/utils/configureStore.ts
index 75bb516bfbe..8681815877f 100644
--- a/server/sonar-web/src/main/js/store/utils/configureStore.js
+++ b/server/sonar-web/src/main/js/store/utils/configureStore.ts
@@ -20,6 +20,9 @@
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
+type RootReducer = typeof import('../rootReducer').default;
+type State = import('../rootReducer').Store;
+
const middlewares = [thunk];
const composed = [];
@@ -27,7 +30,8 @@ if (process.env.NODE_ENV === 'development') {
const { createLogger } = require('redux-logger');
middlewares.push(createLogger());
- composed.push(window.devToolsExtension ? window.devToolsExtension() : f => f);
+ const { devToolsExtension } = window as any;
+ composed.push(devToolsExtension ? devToolsExtension() : (f: Function) => f);
}
const finalCreateStore = compose(
@@ -35,6 +39,6 @@ const finalCreateStore = compose(
...composed
)(createStore);
-export default function configureStore(rootReducer, initialState) {
+export default function configureStore(rootReducer: RootReducer, initialState?: State) {
return finalCreateStore(rootReducer, initialState);
}