]> source.dussan.org Git - sonarqube.git/commitdiff
finish typing redux store and simplify connected components (#675)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 3 Sep 2018 07:21:22 +0000 (09:21 +0200)
committerSonarTech <sonartech@sonarsource.com>
Mon, 3 Sep 2018 18:20:51 +0000 (20:20 +0200)
184 files changed:
server/sonar-web/src/main/js/api/issues.ts
server/sonar-web/src/main/js/api/languages.ts
server/sonar-web/src/main/js/api/organizations.ts
server/sonar-web/src/main/js/api/permissions.ts
server/sonar-web/src/main/js/api/quality-profiles.ts
server/sonar-web/src/main/js/app/components/AdminContainer.tsx
server/sonar-web/src/main/js/app/components/App.tsx
server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
server/sonar-web/src/main/js/app/components/GlobalFooterContainer.tsx
server/sonar-web/src/main/js/app/components/GlobalMessagesContainer.tsx
server/sonar-web/src/main/js/app/components/Landing.tsx
server/sonar-web/src/main/js/app/components/PageTracker.tsx
server/sonar-web/src/main/js/app/components/StartupModal.tsx
server/sonar-web/src/main/js/app/components/embed-docs-modal/ProductNewsMenuItem.tsx
server/sonar-web/src/main/js/app/components/extensions/ExtensionContainer.tsx
server/sonar-web/src/main/js/app/components/extensions/OrganizationPageExtension.tsx
server/sonar-web/src/main/js/app/components/extensions/ProjectAdminPageExtension.js
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavHeader.tsx
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.tsx
server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNav-test.tsx
server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNav-test.tsx.snap
server/sonar-web/src/main/js/app/components/search/Search.d.ts
server/sonar-web/src/main/js/app/flow-types.js [new file with mode: 0644]
server/sonar-web/src/main/js/app/types.ts
server/sonar-web/src/main/js/app/utils/addGlobalSuccessMessage.ts
server/sonar-web/src/main/js/app/utils/getStore.ts
server/sonar-web/src/main/js/app/utils/handleRequiredAuthorization.js
server/sonar-web/src/main/js/app/utils/throwGlobalError.ts
server/sonar-web/src/main/js/apps/about/sonarcloud/SonarCloudPage.tsx
server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx
server/sonar-web/src/main/js/apps/account/organizations/actions.ts
server/sonar-web/src/main/js/apps/background-tasks/components/StatsContainer.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/LanguageFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx
server/sonar-web/src/main/js/apps/component-measures/components/App.js
server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverview.js
server/sonar-web/src/main/js/apps/component-measures/components/MeasureOverviewContainer.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChart.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentsListRow.js
server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.js
server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx
server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/LanguageFacet.tsx
server/sonar-web/src/main/js/apps/marketplace/App.tsx
server/sonar-web/src/main/js/apps/marketplace/AppContainer.tsx
server/sonar-web/src/main/js/apps/organizationMembers/OrganizationMembersContainer.tsx
server/sonar-web/src/main/js/apps/organizations/actions.ts
server/sonar-web/src/main/js/apps/organizations/components/OrganizationAccessContainer.tsx
server/sonar-web/src/main/js/apps/organizations/components/OrganizationContainer.tsx
server/sonar-web/src/main/js/apps/organizations/components/OrganizationDelete.tsx
server/sonar-web/src/main/js/apps/organizations/components/OrganizationEdit.tsx
server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.tsx
server/sonar-web/src/main/js/apps/organizations/forSingleOrganization.tsx
server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationHeaderContainer.tsx
server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMenuContainer.tsx
server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx
server/sonar-web/src/main/js/apps/overview/components/SonarCloudEmptyOverview.tsx
server/sonar-web/src/main/js/apps/overview/meta/MetaContainer.tsx
server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.tsx
server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx
server/sonar-web/src/main/js/apps/permissions/global/components/AllHoldersListContainer.tsx
server/sonar-web/src/main/js/apps/permissions/project/components/App.js
server/sonar-web/src/main/js/apps/permissions/project/components/AppContainer.js
server/sonar-web/src/main/js/apps/permissions/shared/components/PageError.tsx
server/sonar-web/src/main/js/apps/portfolio/components/App.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Subscription.tsx
server/sonar-web/src/main/js/apps/portfolio/components/SubscriptionContainer.tsx
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/Subscription-test.tsx
server/sonar-web/src/main/js/apps/project-admin/key/BulkUpdate.js
server/sonar-web/src/main/js/apps/project-admin/key/Key.js
server/sonar-web/src/main/js/apps/projectBranches/components/AppContainer.ts
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelectorContainer.tsx
server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx
server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganization.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardOrganizationContainer.tsx
server/sonar-web/src/main/js/apps/projects/create/CreateProjectPage.tsx
server/sonar-web/src/main/js/apps/projects/create/ManualProjectCreate.tsx
server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.tsx
server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.tsx
server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx
server/sonar-web/src/main/js/apps/projectsManagement/AppContainer.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/DetailsApp.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/CopyProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/DeleteProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/RenameProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeParentForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-profiles/home/CreateProfileForm.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/RestoreProfileForm.tsx
server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx
server/sonar-web/src/main/js/apps/settings/store/actions.js
server/sonar-web/src/main/js/apps/settings/store/encryptionPage/actions.js
server/sonar-web/src/main/js/apps/settings/store/rootReducer.js
server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingModal.tsx
server/sonar-web/src/main/js/apps/tutorials/onboarding/OnboardingPage.tsx
server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboarding.tsx
server/sonar-web/src/main/js/apps/tutorials/projectOnboarding/ProjectOnboardingPage.tsx
server/sonar-web/src/main/js/apps/users/UsersApp.tsx
server/sonar-web/src/main/js/apps/users/UsersAppContainer.tsx
server/sonar-web/src/main/js/apps/users/UsersList.tsx
server/sonar-web/src/main/js/apps/users/components/UserListItem.tsx
server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx
server/sonar-web/src/main/js/components/charts/LanguageDistributionContainer.tsx
server/sonar-web/src/main/js/components/charts/__tests__/LanguageDistribution-test.tsx
server/sonar-web/src/main/js/components/common/PrivacyBadgeContainer.tsx
server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
server/sonar-web/src/main/js/components/controls/__tests__/HomePageSelect-test.tsx
server/sonar-web/src/main/js/components/issue/Issue.js
server/sonar-web/src/main/js/components/issue/IssueView.js
server/sonar-web/src/main/js/components/issue/actions.js
server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.js
server/sonar-web/src/main/js/components/issue/components/IssueAssign.js
server/sonar-web/src/main/js/components/issue/components/IssueChangelog.js
server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.js
server/sonar-web/src/main/js/components/issue/components/IssueTags.js
server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js
server/sonar-web/src/main/js/components/issue/components/IssueTransition.js
server/sonar-web/src/main/js/components/issue/components/SimilarIssuesFilter.js
server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.js
server/sonar-web/src/main/js/components/issue/components/__tests__/IssueChangelog-test.js
server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentAction-test.js
server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTags-test.js
server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTitleBar-test.js
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.js.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.js.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap
server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js
server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js
server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js
server/sonar-web/src/main/js/components/measure/types.js
server/sonar-web/src/main/js/components/shared/Organization.tsx
server/sonar-web/src/main/js/components/ui/Avatar.tsx
server/sonar-web/src/main/js/store/__tests__/__snapshots__/organizations-test.ts.snap [new file with mode: 0644]
server/sonar-web/src/main/js/store/__tests__/organizations-test.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/appState.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/appState/duck.ts [deleted file]
server/sonar-web/src/main/js/store/globalMessages.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/globalMessages/duck.js [deleted file]
server/sonar-web/src/main/js/store/languages.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/languages/actions.js [deleted file]
server/sonar-web/src/main/js/store/languages/reducer.ts [deleted file]
server/sonar-web/src/main/js/store/metrics.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/metrics/actions.js [deleted file]
server/sonar-web/src/main/js/store/metrics/reducer.js [deleted file]
server/sonar-web/src/main/js/store/organizations.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/organizations/__tests__/__snapshots__/duck-test.ts.snap [deleted file]
server/sonar-web/src/main/js/store/organizations/__tests__/duck-test.ts [deleted file]
server/sonar-web/src/main/js/store/organizations/duck.ts [deleted file]
server/sonar-web/src/main/js/store/rootActions.js [deleted file]
server/sonar-web/src/main/js/store/rootActions.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/rootReducer.js [deleted file]
server/sonar-web/src/main/js/store/rootReducer.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/users.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/users/actions.ts [deleted file]
server/sonar-web/src/main/js/store/users/reducer.ts [deleted file]
server/sonar-web/src/main/js/store/utils/actions.ts [new file with mode: 0644]
server/sonar-web/src/main/js/store/utils/configureStore.js [deleted file]
server/sonar-web/src/main/js/store/utils/configureStore.ts [new file with mode: 0644]

index 6f1e4ed20e0052ccc0ceed075daeb0d1a9eeffd6..40d1222314ad540f34e57350a6d0331611f59541 100644 (file)
@@ -110,7 +110,7 @@ export function searchIssueTags(data: {
 }
 
 export function getIssueChangelog(issue: string): Promise<any> {
-  return getJSON('/api/issues/changelog', { issue }).then(r => r.changelog);
+  return getJSON('/api/issues/changelog', { issue }).then(r => r.changelog, throwGlobalError);
 }
 
 export function getIssueFilters() {
index f5902cdb77bff0452884e61229baf7440879047c..c49c1290672a6c3667beb8b76c141131acfb65dc 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { getJSON } from '../helpers/request';
+import throwGlobalError from '../app/utils/throwGlobalError';
 
 export interface Language {
   key: string;
@@ -25,5 +26,5 @@ export interface Language {
 }
 
 export function getLanguages(): Promise<Language[]> {
-  return getJSON('/api/languages/list').then(r => r.languages);
+  return getJSON('/api/languages/list').then(r => r.languages, throwGlobalError);
 }
index 78d8f63ac1c6e0d78d5c1e43da021b84d6e18b9f..289d6ce99d54b2a97965b9d81f542cbe7cac876f 100644 (file)
@@ -28,7 +28,7 @@ export function getOrganizations(data: {
   organizations: Organization[];
   paging: Paging;
 }> {
-  return getJSON('/api/organizations/search', data);
+  return getJSON('/api/organizations/search', data).catch(throwGlobalError);
 }
 
 export function getOrganization(key: string): Promise<Organization | undefined> {
@@ -48,18 +48,21 @@ interface GetOrganizationNavigation {
 }
 
 export function getOrganizationNavigation(key: string): Promise<GetOrganizationNavigation> {
-  return getJSON('/api/navigation/organization', { organization: key }).then(r => r.organization);
+  return getJSON('/api/navigation/organization', { organization: key }).then(
+    r => r.organization,
+    throwGlobalError
+  );
 }
 
 export function createOrganization(data: OrganizationBase): Promise<Organization> {
   return postJSON('/api/organizations/create', data).then(r => r.organization, throwGlobalError);
 }
 
-export function updateOrganization(key: string, changes: OrganizationBase): Promise<void> {
-  return post('/api/organizations/update', { key, ...changes });
+export function updateOrganization(key: string, changes: OrganizationBase) {
+  return post('/api/organizations/update', { key, ...changes }).catch(throwGlobalError);
 }
 
-export function deleteOrganization(key: string): Promise<void | Response> {
+export function deleteOrganization(key: string) {
   return post('/api/organizations/delete', { key }).catch(throwGlobalError);
 }
 
index e14367bb6a7010e55bf3a98b5eea33374c0e3ef6..116e242e3c23600a6c5f1164402227dc0f8a3f12 100644 (file)
@@ -29,7 +29,7 @@ export function grantPermissionToUser(
   login: string,
   permission: string,
   organization?: string
-): Promise<void> {
+) {
   const data: RequestData = { login, permission };
   if (projectKey) {
     data.projectKey = projectKey;
@@ -37,7 +37,7 @@ export function grantPermissionToUser(
   if (organization && !projectKey) {
     data.organization = organization;
   }
-  return post('/api/permissions/add_user', data);
+  return post('/api/permissions/add_user', data).catch(throwGlobalError);
 }
 
 export function revokePermissionFromUser(
@@ -45,7 +45,7 @@ export function revokePermissionFromUser(
   login: string,
   permission: string,
   organization?: string
-): Promise<void> {
+) {
   const data: RequestData = { login, permission };
   if (projectKey) {
     data.projectKey = projectKey;
@@ -53,7 +53,7 @@ export function revokePermissionFromUser(
   if (organization && !projectKey) {
     data.organization = organization;
   }
-  return post('/api/permissions/remove_user', data);
+  return post('/api/permissions/remove_user', data).catch(throwGlobalError);
 }
 
 export function grantPermissionToGroup(
@@ -61,7 +61,7 @@ export function grantPermissionToGroup(
   groupName: string,
   permission: string,
   organization?: string
-): Promise<void> {
+) {
   const data: RequestData = { groupName, permission };
   if (projectKey) {
     data.projectKey = projectKey;
@@ -69,7 +69,7 @@ export function grantPermissionToGroup(
   if (organization) {
     data.organization = organization;
   }
-  return post('/api/permissions/add_group', data);
+  return post('/api/permissions/add_group', data).catch(throwGlobalError);
 }
 
 export function revokePermissionFromGroup(
@@ -77,7 +77,7 @@ export function revokePermissionFromGroup(
   groupName: string,
   permission: string,
   organization?: string
-): Promise<void> {
+) {
   const data: RequestData = { groupName, permission };
   if (projectKey) {
     data.projectKey = projectKey;
@@ -85,7 +85,7 @@ export function revokePermissionFromGroup(
   if (organization) {
     data.organization = organization;
   }
-  return post('/api/permissions/remove_group', data);
+  return post('/api/permissions/remove_group', data).catch(throwGlobalError);
 }
 
 interface GetPermissionTemplatesResponse {
index bf8fc2b3eeb8f4f9f7087dc0fc163701c6cfeeb8..93e0c1d50647ad74e1d49b21d02e77990781fa4f 100644 (file)
@@ -95,7 +95,8 @@ export function createQualityProfile(data: RequestData): Promise<any> {
     .setData(data)
     .submit()
     .then(checkStatus)
-    .then(parseJSON);
+    .then(parseJSON)
+    .catch(throwGlobalError);
 }
 
 export function restoreQualityProfile(data: RequestData): Promise<any> {
@@ -104,7 +105,8 @@ export function restoreQualityProfile(data: RequestData): Promise<any> {
     .setData(data)
     .submit()
     .then(checkStatus)
-    .then(parseJSON);
+    .then(parseJSON)
+    .catch(throwGlobalError);
 }
 
 export interface ProfileProject {
@@ -128,20 +130,22 @@ export function setDefaultProfile(profileKey: string): Promise<void> {
   return post('/api/qualityprofiles/set_default', { profileKey });
 }
 
-export function renameProfile(key: string, name: string): Promise<void> {
-  return post('/api/qualityprofiles/rename', { key, name });
+export function renameProfile(key: string, name: string) {
+  return post('/api/qualityprofiles/rename', { key, name }).catch(throwGlobalError);
 }
 
 export function copyProfile(fromKey: string, toName: string): Promise<any> {
-  return postJSON('/api/qualityprofiles/copy', { fromKey, toName });
+  return postJSON('/api/qualityprofiles/copy', { fromKey, toName }).catch(throwGlobalError);
 }
 
-export function deleteProfile(profileKey: string): Promise<void> {
-  return post('/api/qualityprofiles/delete', { profileKey });
+export function deleteProfile(profileKey: string) {
+  return post('/api/qualityprofiles/delete', { profileKey }).catch(throwGlobalError);
 }
 
-export function changeProfileParent(profileKey: string, parentKey: string): Promise<void> {
-  return post('/api/qualityprofiles/change_parent', { profileKey, parentKey });
+export function changeProfileParent(profileKey: string, parentKey: string) {
+  return post('/api/qualityprofiles/change_parent', { profileKey, parentKey }).catch(
+    throwGlobalError
+  );
 }
 
 export function getImporters(): Promise<
index 05c330a371f0e853e271013cc3a1495a0bafa7f4..cb2d5b997092c55506bd79372a4619d80a28a147 100644 (file)
@@ -23,9 +23,9 @@ import Helmet from 'react-helmet';
 import { connect } from 'react-redux';
 import MarketplaceContext, { defaultPendingPlugins } from './MarketplaceContext';
 import SettingsNav from './nav/settings/SettingsNav';
-import { getAppState } from '../../store/rootReducer';
+import { getAppState, Store } from '../../store/rootReducer';
 import { getSettingsNavigation } from '../../api/nav';
-import { setAdminPages } from '../../store/appState/duck';
+import { setAdminPages } from '../../store/appState';
 import { translate } from '../../helpers/l10n';
 import { Extension, AppState } from '../types';
 import { PluginPendingResult, getPendingPlugins } from '../../api/plugins';
@@ -121,7 +121,7 @@ class AdminContainer extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   appState: getAppState(state)
 });
 
@@ -129,7 +129,7 @@ const mapDispatchToProps: DispatchToProps = {
   setAdminPages
 };
 
-export default connect<StateProps, DispatchToProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(AdminContainer);
index 22cd429689af6c140958b69d440bce735764b6b1..e5257a4a17128849f1969acc3bac4fcab0388035 100644 (file)
@@ -26,7 +26,7 @@ import { fetchLanguages } from '../../store/rootActions';
 import { fetchMyOrganizations } from '../../apps/account/organizations/actions';
 import { getInstance, isSonarCloud } from '../../helpers/system';
 import { lazyLoad } from '../../components/lazyLoad';
-import { getCurrentUser, getAppState, getGlobalSettingValue } from '../../store/rootReducer';
+import { getCurrentUser, getAppState, getGlobalSettingValue, Store } from '../../store/rootReducer';
 
 const PageTracker = lazyLoad(() => import('./PageTracker'));
 
@@ -104,7 +104,7 @@ class App extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   appState: getAppState(state),
   currentUser: getCurrentUser(state),
   enableGravatar: (getGlobalSettingValue(state, 'sonar.lf.enableGravatar') || {}).value === 'true',
index 00e376c5dfc8b1f299d395fa778b0e4b3206d3a8..e8ee249706d4aae47a0f2ead96983a73f2f388fb 100644 (file)
@@ -315,7 +315,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps = { fetchOrganizations };
 
-export default connect<any, any, any>(
+export default connect(
   null,
   mapDispatchToProps
 )(ComponentContainer);
index 4f079504d72c5724bd0a929578ea711ea228e255..158478034d565e46e92dbd38ecc99e565928a80d 100644 (file)
@@ -19,7 +19,7 @@
  */
 import { connect } from 'react-redux';
 import GlobalFooter from './GlobalFooter';
-import { getAppState } from '../../store/rootReducer';
+import { getAppState, Store } from '../../store/rootReducer';
 import { EditionKey } from '../../apps/marketplace/utils';
 
 interface StateProps {
@@ -28,14 +28,10 @@ interface StateProps {
   sonarqubeVersion?: string;
 }
 
-interface OwnProps {
-  hideLoggedInInfo?: boolean;
-}
-
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   productionDatabase: getAppState(state).productionDatabase,
   sonarqubeEdition: getAppState(state).edition,
   sonarqubeVersion: getAppState(state).version
 });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(GlobalFooter);
+export default connect(mapStateToProps)(GlobalFooter);
index d8366702bc8b4968428a84b00b22e87053967186..e9dfd8a6e08ed75c3a9db0f6b349416072a285c5 100644 (file)
  */
 import { connect } from 'react-redux';
 import GlobalMessages from '../../components/controls/GlobalMessages';
-import { getGlobalMessages } from '../../store/rootReducer';
-import { closeGlobalMessage } from '../../store/globalMessages/duck';
+import { getGlobalMessages, Store } from '../../store/rootReducer';
+import { closeGlobalMessage } from '../../store/globalMessages';
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   messages: getGlobalMessages(state)
 });
 
index d4f80be9ab820d96caff8d2e6573158c4a06e62a..f96be5fc011c697d4a8b8bb4c065a8adf9971f21 100644 (file)
@@ -22,7 +22,7 @@ import * as PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import { Location } from 'history';
 import { CurrentUser, isLoggedIn } from '../types';
-import { getCurrentUser } from '../../store/rootReducer';
+import { getCurrentUser, Store } from '../../store/rootReducer';
 import { getHomePageUrl } from '../../helpers/urls';
 
 interface StateProps {
@@ -57,8 +57,8 @@ class Landing extends React.PureComponent<StateProps & OwnProps> {
   }
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state)
 });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(Landing);
+export default connect(mapStateToProps)(Landing);
index a7699df640fb499dcfa2ba12f10fec4a35a22564..2ae8c85026be00db7d7f66e85db4333e75994d85 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import * as GoogleAnalytics from 'react-ga';
 import { withRouter, WithRouterProps } from 'react-router';
 import { connect } from 'react-redux';
-import { getGlobalSettingValue } from '../../store/rootReducer';
+import { getGlobalSettingValue, Store } from '../../store/rootReducer';
 
 interface StateProps {
   trackingId?: string;
@@ -59,8 +59,8 @@ export class PageTracker extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   trackingId: (getGlobalSettingValue(state, 'sonar.analytics.trackingId') || {}).value
 });
 
-export default withRouter<{}>(connect<StateProps>(mapStateToProps)(PageTracker));
+export default withRouter(connect(mapStateToProps)(PageTracker));
index 9383ec2539e9e75c1fb96ac2a76eb532444b3020..ee49e969d4969b8ad977a7782a64f3c9c8962007 100644 (file)
@@ -23,8 +23,8 @@ import { connect } from 'react-redux';
 import { CurrentUser, isLoggedIn, Organization } from '../types';
 import { differenceInDays, parseDate, toShortNotSoISOString } from '../../helpers/dates';
 import { EditionKey } from '../../apps/marketplace/utils';
-import { getCurrentUser, getAppState } from '../../store/rootReducer';
-import { skipOnboarding as skipOnboardingAction } from '../../store/users/actions';
+import { getCurrentUser, getAppState, Store } from '../../store/rootReducer';
+import { skipOnboarding as skipOnboardingAction } from '../../store/users';
 import { showLicense } from '../../api/marketplace';
 import { hasMessage } from '../../helpers/l10n';
 import { save, get } from '../../helpers/storage';
@@ -49,7 +49,7 @@ const TeamOnboardingModal = lazyLoad(() =>
 );
 
 interface StateProps {
-  canAdmin: boolean;
+  canAdmin?: boolean;
   currentEdition?: EditionKey;
   currentUser: CurrentUser;
 }
@@ -215,7 +215,7 @@ export class StartupModal extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   canAdmin: getAppState(state).canAdmin,
   currentEdition: getAppState(state).edition,
   currentUser: getCurrentUser(state)
@@ -223,7 +223,7 @@ const mapStateToProps = (state: any): StateProps => ({
 
 const mapDispatchToProps: DispatchProps = { skipOnboardingAction };
 
-export default connect<StateProps, DispatchProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(StartupModal);
index e87e82bad4f571c7d62eb6ac410d33a42b30feca..3037367c20ee71583dfe5082fad9277cbe224810 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { connect } from 'react-redux';
 import { fetchPrismicRefs, fetchPrismicNews, PrismicNews } from '../../../api/news';
-import { getGlobalSettingValue } from '../../../store/rootReducer';
+import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
 import DateFormatter from '../../../components/intl/DateFormatter';
 import ChevronRightIcon from '../../../components/icons-components/ChevronRightcon';
 import PlaceholderBar from '../../../components/ui/PlaceholderBar';
@@ -124,8 +124,8 @@ export class ProductNewsMenuItem extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   accessToken: (getGlobalSettingValue(state, 'sonar.prismic.accessToken') || {}).value
 });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(ProductNewsMenuItem);
+export default connect(mapStateToProps)(ProductNewsMenuItem);
index c3c56057669f06d6e7ac316a73972c9f1f66e5b9..a3e36e6c76a50fee5f075cb1049e73d71070a526 100644 (file)
  */
 import { connect } from 'react-redux';
 import Extension from './Extension';
-import { getCurrentUser } from '../../../store/rootReducer';
-import { addGlobalErrorMessage } from '../../../store/globalMessages/duck';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
+import { addGlobalErrorMessage } from '../../../store/globalMessages';
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state)
 });
 
 const mapDispatchToProps = { onFail: addGlobalErrorMessage };
 
-export default connect<any, any, any>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(Extension);
index c38e996c2a38476ac31af62141803b104a328ab8..a12e39986cd271fe067a2a98348d8e7cb9e78730 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import ExtensionContainer from './ExtensionContainer';
 import NotFound from '../NotFound';
-import { getOrganizationByKey } from '../../../store/rootReducer';
+import { getOrganizationByKey, Store } from '../../../store/rootReducer';
 import { fetchOrganization } from '../../../apps/organizations/actions';
 import { Organization } from '../../types';
 
@@ -74,13 +74,13 @@ class OrganizationPageExtension extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps) => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps) => ({
   organization: getOrganizationByKey(state, ownProps.params.organizationKey)
 });
 
 const mapDispatchToProps = { fetchOrganization };
 
-export default connect<StateToProps, DispatchProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(OrganizationPageExtension);
index 45c7a677a18b0b23dee3ccd95cbe597df059a959..e39394c6d9120d2ffb8c951c34400a92a5f6ab14 100644 (file)
@@ -22,7 +22,7 @@ import React from 'react';
 import { connect } from 'react-redux';
 import ExtensionContainer from './ExtensionContainer';
 import NotFound from '../NotFound';
-import { addGlobalErrorMessage } from '../../../store/globalMessages/duck';
+import { addGlobalErrorMessage } from '../../../store/globalMessages';
 
 /*::
 type Props = {
index e616861f278a0e75c423d87038487a61b168b0ba..f32e3dfa30875c955a451af61eb805e064f9254a 100644 (file)
@@ -23,7 +23,11 @@ import { Link } from 'react-router';
 import ComponentNavBranch from './ComponentNavBranch';
 import { Component, Organization, BranchLike, Breadcrumb } from '../../../types';
 import QualifierIcon from '../../../../components/icons-components/QualifierIcon';
-import { getOrganizationByKey, areThereCustomOrganizations } from '../../../../store/rootReducer';
+import {
+  getOrganizationByKey,
+  areThereCustomOrganizations,
+  Store
+} from '../../../../store/rootReducer';
 import OrganizationAvatar from '../../../../components/common/OrganizationAvatar';
 import OrganizationHelmet from '../../../../components/common/OrganizationHelmet';
 import OrganizationLink from '../../../../components/ui/OrganizationLink';
@@ -34,7 +38,7 @@ import { isSonarCloud } from '../../../../helpers/system';
 
 interface StateProps {
   organization?: Organization;
-  shouldOrganizationBeDisplayed: boolean;
+  shouldOrganizationBeDisplayed?: boolean;
 }
 
 interface OwnProps {
@@ -118,7 +122,7 @@ function renderBreadcrumbs(breadcrumbs: Breadcrumb[]) {
   });
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps): StateProps => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps): StateProps => ({
   organization: getOrganizationByKey(state, ownProps.component.organization),
   shouldOrganizationBeDisplayed: areThereCustomOrganizations(state)
 });
index 7b2f2e2c2219e60f6d086abb7a917b05ea1ca1d3..51babd7921bb3d42c17c6258a29162aca532b38f 100644 (file)
@@ -40,7 +40,7 @@ import {
   isPullRequest
 } from '../../../../helpers/branches';
 import { translate } from '../../../../helpers/l10n';
-import { getCurrentUser } from '../../../../store/rootReducer';
+import { getCurrentUser, Store } from '../../../../store/rootReducer';
 
 interface StateProps {
   currentUser: CurrentUser;
@@ -121,7 +121,7 @@ function getCurrentPage(component: Component, branchLike: BranchLike | undefined
   return currentPage;
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   currentUser: getCurrentUser(state)
 });
 
index 81619581862a8919773efe4119ada81875c0fa48..396050e473146bb556c35b1f89450304c4542dc6 100644 (file)
@@ -30,7 +30,7 @@ import * as theme from '../../../theme';
 import { isLoggedIn, CurrentUser, AppState } from '../../../types';
 import NavBar from '../../../../components/nav/NavBar';
 import { lazyLoad } from '../../../../components/lazyLoad';
-import { getCurrentUser, getAppState } from '../../../../store/rootReducer';
+import { getCurrentUser, getAppState, Store } from '../../../../store/rootReducer';
 import { SuggestionLink } from '../../embed-docs-modal/SuggestionsProvider';
 import { isSonarCloud } from '../../../../helpers/system';
 import './GlobalNav.css';
@@ -38,7 +38,7 @@ import './GlobalNav.css';
 const GlobalNavPlus = lazyLoad(() => import('./GlobalNavPlus'));
 
 interface StateProps {
-  appState: AppState;
+  appState: Pick<AppState, 'canAdmin' | 'globalPages' | 'organizationsEnabled' | 'qualifiers'>;
   currentUser: CurrentUser;
 }
 
@@ -78,9 +78,9 @@ export class GlobalNav extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   currentUser: getCurrentUser(state),
   appState: getAppState(state)
 });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(GlobalNav);
+export default connect(mapStateToProps)(GlobalNav);
index 7c0121ca23e1919df6603256c28cf760f369e78d..876a1ccc62f0eb1adc678aef8898a20368fad63f 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { Link } from 'react-router';
 import { connect } from 'react-redux';
-import { getGlobalSettingValue } from '../../../../store/rootReducer';
+import { getGlobalSettingValue, Store } from '../../../../store/rootReducer';
 import { translate } from '../../../../helpers/l10n';
 import { getBaseUrl } from '../../../../helpers/urls';
 
@@ -50,9 +50,9 @@ export function SonarCloudNavBranding() {
   );
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   customLogoUrl: (getGlobalSettingValue(state, 'sonar.lf.logoUrl') || {}).value,
   customLogoWidth: (getGlobalSettingValue(state, 'sonar.lf.logoWidthPx') || {}).value
 });
 
-export default connect<StateProps>(mapStateToProps)(GlobalNavBranding);
+export default connect(mapStateToProps)(GlobalNavBranding);
index 6bb9590573ac96d5dee38914f5cbc39b9c5400d4..8856840d3f1c710e18579c4426069c033ea837ee 100644 (file)
@@ -29,7 +29,7 @@ import DropdownIcon from '../../../../components/icons-components/DropdownIcon';
 import { isSonarCloud } from '../../../../helpers/system';
 
 interface Props {
-  appState: AppState;
+  appState: Pick<AppState, 'canAdmin' | 'globalPages' | 'organizationsEnabled' | 'qualifiers'>;
   currentUser: CurrentUser;
   location: { pathname: string };
 }
index e02817a046dafe482dff136cf201316203aedbf3..11f8f734dd7858615118d1d43820419b7e30dc99 100644 (file)
 import { connect } from 'react-redux';
 import GlobalNavUser from './GlobalNavUser';
 import { Organization } from '../../../types';
-import { getMyOrganizations } from '../../../../store/rootReducer';
+import { getMyOrganizations, Store } from '../../../../store/rootReducer';
 
 interface StateProps {
   organizations: Organization[];
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   organizations: getMyOrganizations(state)
 });
 
index 17ed417200a7aeee579756340627f708c20df3e2..3410e31581faece74ba70b19472bdcf4f32c8424 100644 (file)
@@ -24,7 +24,12 @@ import { isSonarCloud } from '../../../../../helpers/system';
 
 jest.mock('../../../../../helpers/system', () => ({ isSonarCloud: jest.fn() }));
 
-const appState = { qualifiers: [] };
+const appState: GlobalNav['props']['appState'] = {
+  globalPages: [],
+  canAdmin: false,
+  organizationsEnabled: false,
+  qualifiers: []
+};
 const currentUser = { isLoggedIn: false };
 const location = { pathname: '' };
 
index 7f6362e4750f767987e189e707b0054a107687c6..1ffdab8f252b27f874930b15f605b0237e3140e5 100644 (file)
@@ -10,6 +10,9 @@ exports[`should render for SonarCloud 1`] = `
   <GlobalNavMenu
     appState={
       Object {
+        "canAdmin": false,
+        "globalPages": Array [],
+        "organizationsEnabled": false,
         "qualifiers": Array [],
       }
     }
@@ -47,6 +50,9 @@ exports[`should render for SonarCloud 1`] = `
     <Search
       appState={
         Object {
+          "canAdmin": false,
+          "globalPages": Array [],
+          "organizationsEnabled": false,
           "qualifiers": Array [],
         }
       }
@@ -59,6 +65,9 @@ exports[`should render for SonarCloud 1`] = `
     <Connect(GlobalNavUser)
       appState={
         Object {
+          "canAdmin": false,
+          "globalPages": Array [],
+          "organizationsEnabled": false,
           "qualifiers": Array [],
         }
       }
@@ -88,6 +97,9 @@ exports[`should render for SonarQube 1`] = `
   <GlobalNavMenu
     appState={
       Object {
+        "canAdmin": false,
+        "globalPages": Array [],
+        "organizationsEnabled": false,
         "qualifiers": Array [],
       }
     }
@@ -118,6 +130,9 @@ exports[`should render for SonarQube 1`] = `
     <Search
       appState={
         Object {
+          "canAdmin": false,
+          "globalPages": Array [],
+          "organizationsEnabled": false,
           "qualifiers": Array [],
         }
       }
@@ -130,6 +145,9 @@ exports[`should render for SonarQube 1`] = `
     <Connect(GlobalNavUser)
       appState={
         Object {
+          "canAdmin": false,
+          "globalPages": Array [],
+          "organizationsEnabled": false,
           "qualifiers": Array [],
         }
       }
index b2590dc00e6ae5eb5852094642f4184ccaa1f995..58ceb74bdc0511136a5346f74c9e255a5eed4bd3 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { CurrentUser, AppState } from '../../types';
 
 export interface Props {
-  appState: AppState;
+  appState: Pick<AppState, 'organizationsEnabled'>;
   currentUser: CurrentUser;
 }
 
diff --git a/server/sonar-web/src/main/js/app/flow-types.js b/server/sonar-web/src/main/js/app/flow-types.js
new file mode 100644 (file)
index 0000000..7af83c0
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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
+}; */
index b39b6e62427424a08cae6a26ab2d377e40ec012a..3918a750ed3e1e17d57acba9d1120913ab53d93e 100644 (file)
@@ -17,6 +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.
  */
+import { EditionKey } from '../apps/marketplace/utils';
+
 export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 
 // Type ordered alphabetically to prevent merge conflicts
@@ -34,9 +36,14 @@ export interface AppState {
   authorizationError?: boolean;
   branchesEnabled?: boolean;
   canAdmin?: boolean;
+  defaultOrganization: string;
+  edition: EditionKey;
   globalPages?: Extension[];
   organizationsEnabled?: boolean;
+  productionDatabase: boolean;
   qualifiers: string[];
+  standalone?: boolean;
+  version: string;
 }
 
 export interface Branch {
index 587316afa26bed33a8028d15e4a356cb36dda1a6..2e4b82d7dc390933441ba35228236e0fbdad0cc0 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import getStore from './getStore';
-import * as globalMessages from '../../store/globalMessages/duck';
+import * as globalMessages from '../../store/globalMessages';
 
 export default function addGlobalSuccessMessage(message: string): void {
   const store = getStore();
index 477fc5a57b446d906b66ba86bb7f1d0b89f6ecf5..dc24f943b469c9cfcf678c6baf84b84322b20387 100644 (file)
@@ -19,9 +19,9 @@
  */
 import { Store } from 'redux';
 import { AppState, CurrentUser } from '../types';
-import { setAppState } from '../../store/appState/duck';
+import { setAppState } from '../../store/appState';
 import rootReducer from '../../store/rootReducer';
-import { receiveCurrentUser } from '../../store/users/actions';
+import { receiveCurrentUser } from '../../store/users';
 import configureStore from '../../store/utils/configureStore';
 
 let store: Store<any>;
index c264596dcd0611517aa34cf54037d94a0ad6dc21..eaa2cf81b75959684569e119681135f6a203bbee 100644 (file)
@@ -20,7 +20,7 @@
 // @flow
 import getStore from './getStore';
 import getHistory from './getHistory';
-import { requireAuthorization } from '../../store/appState/duck';
+import { requireAuthorization } from '../../store/appState';
 
 export default () => {
   const store = getStore();
index 4d12df06e77c844ea74605b136f06d3f7887216c..264a3eaac88b6e837e2d7feaef51670c45a1ba1d 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import getStore from './getStore';
-import { onFail } from '../../store/rootActions';
+import { parseError } from '../../helpers/request';
+import { addGlobalErrorMessage } from '../../store/globalMessages';
 
-export default function throwGlobalError({ response }: { response: Response }): Promise<Response> {
+export default function throwGlobalError(error: { response: Response }): Promise<Response> {
   const store = getStore();
-  onFail(store.dispatch)({ response });
-  return Promise.reject(response);
+
+  // eslint-disable-next-line promise/no-promise-in-callback
+  parseError(error)
+    .then(message => {
+      store.dispatch(addGlobalErrorMessage(message));
+    })
+    .catch(() => {});
+
+  return Promise.reject(error.response);
 }
index 84b4a99ade77e6a73a0f965e73ab09a079c73f78..04e14901929b9c1be457137fd649cfd814348d51 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import { withRouter, WithRouterProps } from 'react-router';
 import Footer from './Footer';
-import { getCurrentUser, getMyOrganizations } from '../../../store/rootReducer';
+import { getCurrentUser, getMyOrganizations, Store } from '../../../store/rootReducer';
 import { CurrentUser, Organization } from '../../../app/types';
 import GlobalContainer from '../../../app/components/GlobalContainer';
 
@@ -57,11 +57,9 @@ class SonarCloudPage extends React.Component<Props> {
   }
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state),
   userOrganizations: getMyOrganizations(state)
 });
 
-export default withRouter<OwnProps>(
-  connect<StateProps, {}, OwnProps>(mapStateToProps)(SonarCloudPage)
-);
+export default withRouter<OwnProps>(connect(mapStateToProps)(SonarCloudPage));
index c3768ee5e5c9b46593582af909dd48696988b767..60d477890436cbfc55ef693edb266cb3b8b52047 100644 (file)
@@ -24,14 +24,19 @@ import OrganizationsList from './OrganizationsList';
 import CreateOrganizationForm from './CreateOrganizationForm';
 import { fetchIfAnyoneCanCreateOrganizations } from './actions';
 import { translate } from '../../../helpers/l10n';
-import { getAppState, getMyOrganizations, getGlobalSettingValue } from '../../../store/rootReducer';
+import {
+  getAppState,
+  getMyOrganizations,
+  getGlobalSettingValue,
+  Store
+} from '../../../store/rootReducer';
 import { Organization } from '../../../app/types';
 import { Button } from '../../../components/ui/buttons';
 
 interface StateProps {
   anyoneCanCreate?: { value: string };
-  canAdmin: boolean;
-  organizations: Array<Organization>;
+  canAdmin?: boolean;
+  organizations: Organization[];
 }
 
 interface DispatchProps {
@@ -110,7 +115,7 @@ class UserOrganizations extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   anyoneCanCreate: getGlobalSettingValue(state, 'sonar.organizations.anyoneCanCreate'),
   canAdmin: getAppState(state).canAdmin,
   organizations: getMyOrganizations(state)
index 4361e0d0849dd698ed6133a3702c5e61c7fb34f7..7c6e29f79901ff169980f984c412692cd6d32159 100644 (file)
  */
 import { Dispatch } from 'redux';
 import { getOrganizations } from '../../../api/organizations';
-import { receiveMyOrganizations } from '../../../store/organizations/duck';
+import { receiveMyOrganizations } from '../../../store/organizations';
 import { getValues } from '../../../api/settings';
 import { receiveValues } from '../../settings/store/values/actions';
+import { Store } from '../../../store/rootReducer';
 
-export const fetchMyOrganizations = () => (dispatch: Dispatch<any>) => {
+export const fetchMyOrganizations = () => (dispatch: Dispatch<Store>) => {
   return getOrganizations({ member: true }).then(({ organizations }) => {
     return dispatch(receiveMyOrganizations(organizations));
   });
 };
 
-export const fetchIfAnyoneCanCreateOrganizations = () => (dispatch: Dispatch<any>) => {
+export const fetchIfAnyoneCanCreateOrganizations = () => (dispatch: Dispatch<Store>) => {
   return getValues({ keys: 'sonar.organizations.anyoneCanCreate' }).then(values => {
     dispatch(receiveValues(values, undefined));
   });
index 09a9d8153ef2821bfcbb7eb6371cbd1ff78c1703..f4ab2473ec5295dd891b43be1049d53173d17878 100644 (file)
@@ -19,9 +19,9 @@
  */
 import { connect } from 'react-redux';
 import Stats from './Stats';
-import { getAppState } from '../../../store/rootReducer';
+import { getAppState, Store } from '../../../store/rootReducer';
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   isSystemAdmin: !!getAppState(state).canAdmin
 });
 
index 8e17a963e7096913eef6ac32b4d264cd33e4d7fb..685d05839402c06aefff9221b8d3fbfc775b1da7 100644 (file)
@@ -51,7 +51,7 @@ import FiltersHeader from '../../../components/common/FiltersHeader';
 import SearchBox from '../../../components/controls/SearchBox';
 import { searchRules, getRulesApp } from '../../../api/rules';
 import { searchQualityProfiles, Profile } from '../../../api/quality-profiles';
-import { getCurrentUser, getMyOrganizations } from '../../../store/rootReducer';
+import { getCurrentUser, getMyOrganizations, Store } from '../../../store/rootReducer';
 import { translate } from '../../../helpers/l10n';
 import { RawQuery } from '../../../helpers/query';
 import { scrollToElement } from '../../../helpers/scrolling';
@@ -634,9 +634,9 @@ function parseFacets(rawFacets: { property: string; values: { count: number; val
   return facets;
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state),
   userOrganizations: getMyOrganizations(state)
 });
 
-export default withRouter(connect<StateToProps, {}, OwnProps>(mapStateToProps)(App));
+export default withRouter(connect(mapStateToProps)(App));
index 5f0a4132697486c790c596f9dd2fa80eebab45a2..252db202d06f3660ceba4ac90742148c4987cc4f 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import { uniqBy } from 'lodash';
 import { BasicProps } from './Facet';
-import { getLanguages } from '../../../store/rootReducer';
+import { getLanguages, Store } from '../../../store/rootReducer';
 import ListStyleFacet from '../../../components/facet/ListStyleFacet';
 import { translate } from '../../../helpers/l10n';
 import { highlightTerm } from '../../../helpers/search';
@@ -92,7 +92,7 @@ class LanguageFacet extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   installedLanguages: Object.values(getLanguages(state))
 });
 
index a32e7293c2688ae580a6db9e9e9e10bab1895a90..238aea7a35b2e8cc32b8bed39a6dcc715a156ee0 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { connect } from 'react-redux';
 import Facet, { BasicProps } from './Facet';
-import { getLanguages } from '../../../store/rootReducer';
+import { getLanguages, Store } from '../../../store/rootReducer';
 
 interface StateProps {
   referencedLanguages: { [language: string]: { key: string; name: string } };
@@ -69,7 +69,7 @@ class RepositoryFacet extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   referencedLanguages: getLanguages(state)
 });
 
index a9e552dc77d8b7cee3b1f503713707ad27b102fc..f866fd0dd6042186e147daabd3d18b5c1f8fa48d 100644 (file)
@@ -36,7 +36,7 @@ import {
 import { getDisplayMetrics } from '../../../helpers/measures';
 /*:: import type { Component, Query, Period } from '../types'; */
 /*:: import type { RawQuery } from '../../../helpers/query'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 /*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
 import '../../../components/search-navigator.css';
 import '../style.css';
index a16978f8c5d02c775e5c8bfbe1ec88f5bc7438a1..c39214fba0bd9af3028fad96c002f5d1e621ca6a 100644 (file)
@@ -31,7 +31,7 @@ import { getMeasureHistoryUrl } from '../../../helpers/urls';
 import { isDiffMetric } from '../../../helpers/measures';
 /*:: import type { Component, Period } from '../types'; */
 /*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 /*:: type Props = {|
   branchLike?: { id?: string; name: string },
index 83e59016ccbdf9cd5864940bf60573852ef27d08..495626eaededc53e7ef3bfe2235f7f297cf0d30e 100644 (file)
@@ -30,7 +30,7 @@ import { enhanceComponent, getBubbleMetrics, isFileType } from '../utils';
 import { getBranchLikeQuery } from '../../../helpers/branches';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 /*:: import type { Component, ComponentEnhanced, Paging, Period } from '../types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 /*:: type Props = {|
   branchLike?: { id?: string; name: string },
index 141cb9839db6580959896c8d70d85fe91855fe92..db4caed494760596c9daa6cc5265d20cd5a65b43 100644 (file)
@@ -26,7 +26,7 @@ import { isViewType } from '../utils';
 import { getBranchLikeQuery } from '../../../helpers/branches';
 /*:: import type { Component, Period, Query } from '../types'; */
 /*:: import type { RawQuery } from '../../../helpers/query'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 /*:: type Props = {|
   branchLike?: { id?: string; name: string },
index 9031eacd7afefa98d71f3a86c073c93d9f396cdf..7a6fe3264ede7aaa5efb021c07ba4cc7d9d2c477 100644 (file)
@@ -33,7 +33,7 @@ import {
 import { getBubbleMetrics, getBubbleYDomain, isProjectOverview } from '../utils';
 import { RATING_COLORS } from '../../../helpers/constants';
 /*:: import type { Component, ComponentEnhanced } from '../types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 const HEIGHT = 500;
 
index fc268fa342512b140b3ec1fd1783768e5c569604..6ec4749855ebd2e6b224ad201266c8c6c7abb14a 100644 (file)
@@ -32,7 +32,7 @@ import {
 } from '../../../helpers/urls';
 import { translate } from '../../../helpers/l10n';
 /*:: import type { Component, ComponentEnhanced } from '../types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 /*:: type Props = {
   branchLike?: { id?: string; name: string },
index 891bd5b574746b3e8de87a1a2b513ddaa744ee87..49024fe6f07ac18bf850436f7f3523e36d7beddc 100644 (file)
@@ -23,7 +23,7 @@ import classNames from 'classnames';
 import ComponentCell from './ComponentCell';
 import MeasureCell from './MeasureCell';
 /*:: import type { Component, ComponentEnhanced } from '../types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 /*:: type Props = {|
   branchLike?: { id?: string; name: string },
index da55b93966d233cb5276b4785b9eb4d068f9890f..8a984c5001fe8a878444b00ca3ccb71d582806d4 100644 (file)
@@ -23,7 +23,7 @@ import Measure from '../../../components/measure/Measure';
 import { isDiffMetric } from '../../../helpers/measures';
 /*:: import type { ComponentEnhanced } from '../types'; */
 /*:: import type { MeasureEnhanced } from '../../../components/measure/types'; */
-/*:: import type { Metric } from '../../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../../app/flow-types'; */
 
 /*:: type Props = {
   component: ComponentEnhanced,
index b5c65fd6ed5051883a89e2a9987f778021b31a9b..ed8f05fec887d2ac842ead3ce1bc96daaaa19f7c 100644 (file)
@@ -22,7 +22,7 @@ import { connect } from 'react-redux';
 import AppContainer from './components/AppContainer';
 import { CurrentUser, isLoggedIn } from '../../app/types';
 import { RawQuery } from '../../helpers/query';
-import { getCurrentUser } from '../../store/rootReducer';
+import { getCurrentUser, Store } from '../../store/rootReducer';
 import { isSonarCloud } from '../../helpers/system';
 
 interface StateProps {
@@ -38,8 +38,8 @@ function IssuesPage({ currentUser, location }: Props) {
   return <AppContainer location={location} myIssues={myIssues} />;
 }
 
-const stateToProps = (state: any) => ({
+const stateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state)
 });
 
-export default connect<StateProps>(stateToProps)(IssuesPage);
+export default connect(stateToProps)(IssuesPage);
index a2c5a09d56af54fdfbed97e9b3fcff4af2feabb4..929acb305c0f054eebfcdec6a6ee9dc592929fb0 100644 (file)
@@ -27,24 +27,25 @@ import throwGlobalError from '../../../app/utils/throwGlobalError';
 import {
   getCurrentUser,
   areThereCustomOrganizations,
-  getMyOrganizations
+  getMyOrganizations,
+  Store
 } from '../../../store/rootReducer';
 import { lazyLoad } from '../../../components/lazyLoad';
 import { parseIssueFromResponse } from '../../../helpers/issues';
 import { RawQuery } from '../../../helpers/query';
-import { receiveOrganizations } from '../../../store/organizations/duck';
+import { receiveOrganizations } from '../../../store/organizations';
 
 interface StateProps {
   currentUser: CurrentUser;
   userOrganizations: Organization[];
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   currentUser: getCurrentUser(state),
   userOrganizations: getMyOrganizations(state)
 });
 
-const fetchIssueOrganizations = (organizationKeys: string[]) => (dispatch: Dispatch<any>) => {
+const fetchIssueOrganizations = (organizationKeys: string[]) => (dispatch: Dispatch<Store>) => {
   if (!organizationKeys.length) {
     return Promise.resolve();
   }
@@ -58,7 +59,7 @@ const fetchIssueOrganizations = (organizationKeys: string[]) => (dispatch: Dispa
 const fetchIssues = (query: RawQuery, requestOrganizations = true) => (
   // use `Function` to be able to do `dispatch(...).then(...)`
   dispatch: Function,
-  getState: () => any
+  getState: () => Store
 ) => {
   const organizationsEnabled = areThereCustomOrganizations(getState());
   return searchIssues({ ...query, additionalFields: '_all' })
@@ -87,13 +88,7 @@ interface DispatchProps {
 // have to type cast this, because of async action
 const mapDispatchToProps = { fetchIssues: fetchIssues as any } as DispatchProps;
 
-interface OwnProps {
-  location: { pathname: string; query: RawQuery };
-  hideAuthorFacet?: boolean;
-  myIssues?: boolean;
-}
-
-export default connect<StateProps, DispatchProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(lazyLoad(() => import('./App')));
index b5d2fb47474151134324a3f5802699c962a56328..2dca1a25952032703178b5d49b7e191c3942fb49 100644 (file)
@@ -22,7 +22,7 @@ import { uniqBy, omit } from 'lodash';
 import { connect } from 'react-redux';
 import ListStyleFacet from '../../../components/facet/ListStyleFacet';
 import { Query, ReferencedLanguage, Facet } from '../utils';
-import { getLanguages } from '../../../store/rootReducer';
+import { getLanguages, Store } from '../../../store/rootReducer';
 import { translate } from '../../../helpers/l10n';
 import { highlightTerm } from '../../../helpers/search';
 
@@ -107,7 +107,7 @@ class LanguageFacet extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   installedLanguages: Object.values(getLanguages(state))
 });
 
index 394c96e750ef80ab4257e43fd64fb42a559b7210..2233c8542e16d22a3f29eddfd4533e21acd29e38 100644 (file)
@@ -45,7 +45,7 @@ export interface Props {
   fetchPendingPlugins: () => void;
   location: { pathname: string; query: RawQuery };
   pendingPlugins: PluginPendingResult;
-  standaloneMode: boolean;
+  standaloneMode?: boolean;
   updateCenterActive: boolean;
 }
 
index c68c26b11c96e3540f8625915f6ce9b883f8cd58..76627ae77bd69a6231a6a6a7ecf6f1719b45535a 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import App from './App';
 import { EditionKey } from './utils';
-import { getAppState, getGlobalSettingValue } from '../../store/rootReducer';
+import { getAppState, getGlobalSettingValue, Store } from '../../store/rootReducer';
 import { RawQuery } from '../../helpers/query';
 import MarketplaceContext from '../../app/components/MarketplaceContext';
 
@@ -31,11 +31,11 @@ interface OwnProps {
 
 interface StateToProps {
   currentEdition?: EditionKey;
-  standaloneMode: boolean;
+  standaloneMode?: boolean;
   updateCenterActive: boolean;
 }
 
-const mapStateToProps = (state: any) => {
+const mapStateToProps = (state: Store) => {
   return {
     currentEdition: getAppState(state).edition,
     standaloneMode: getAppState(state).standalone,
@@ -52,4 +52,4 @@ const WithMarketplaceContext = (props: StateToProps & OwnProps) => (
   </MarketplaceContext.Consumer>
 );
 
-export default connect<StateToProps, {}, OwnProps>(mapStateToProps)(WithMarketplaceContext);
+export default connect(mapStateToProps)(WithMarketplaceContext);
index 6f13da8a3c2956b15d7c31d551b30ecf5dbfd407..c2b0821b83e717f83c98950b1face261070d5f18 100644 (file)
@@ -20,7 +20,7 @@
 import { connect } from 'react-redux';
 import OrganizationMembers from './OrganizationMembers';
 import { Organization } from '../../app/types';
-import { getOrganizationByKey } from '../../store/rootReducer';
+import { getOrganizationByKey, Store } from '../../store/rootReducer';
 
 interface OwnProps {
   params: { organizationKey: string };
@@ -30,11 +30,8 @@ interface StateProps {
   organization: Organization;
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps): StateProps => {
-  const { organizationKey } = ownProps.params;
-  return {
-    organization: getOrganizationByKey(state, organizationKey)!
-  };
+const mapStateToProps = (state: Store, ownProps: OwnProps): StateProps => {
+  return { organization: getOrganizationByKey(state, ownProps.params.organizationKey) };
 };
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(OrganizationMembers);
+export default connect(mapStateToProps)(OrganizationMembers);
index ee00b6b3147ef9c66949335b17ada221417bf8ba..682774ff3fa870e4f340d0b9d15eb1e373279e45 100644 (file)
  */
 import { Dispatch } from 'redux';
 import * as api from '../../api/organizations';
-import * as actions from '../../store/organizations/duck';
-import { onFail } from '../../store/rootActions';
-import { addGlobalSuccessMessage } from '../../store/globalMessages/duck';
+import * as actions from '../../store/organizations';
+import { addGlobalSuccessMessage } from '../../store/globalMessages';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 import { Organization, OrganizationBase } from '../../app/types';
+import { Store } from '../../store/rootReducer';
 
-const onRejected = (dispatch: Dispatch<any>) => (error: any) => {
-  onFail(dispatch)(error);
-  return Promise.reject(error);
-};
-
-export const fetchOrganization = (key: string) => (dispatch: Dispatch<any>) => {
+export const fetchOrganization = (key: string) => (dispatch: Dispatch<Store>) => {
   return Promise.all([api.getOrganization(key), api.getOrganizationNavigation(key)]).then(
     ([organization, navigation]) => {
       if (organization) {
         const organizationWithPermissions = { ...organization, ...navigation };
         dispatch(actions.receiveOrganizations([organizationWithPermissions]));
       }
-    },
-    onFail(dispatch)
+    }
   );
 };
 
-export const createOrganization = (organization: OrganizationBase) => (dispatch: Dispatch<any>) => {
+export const createOrganization = (organization: OrganizationBase) => (
+  dispatch: Dispatch<Store>
+) => {
   return api.createOrganization(organization).then((organization: Organization) => {
     dispatch(actions.createOrganization(organization));
     dispatch(
       addGlobalSuccessMessage(translateWithParameters('organization.created', organization.name))
     );
     return organization;
-  }, onRejected(dispatch));
+  });
 };
 
 export const updateOrganization = (key: string, changes: OrganizationBase) => (
-  dispatch: Dispatch<any>
+  dispatch: Dispatch<Store>
 ) => {
   return api.updateOrganization(key, changes).then(() => {
     dispatch(actions.updateOrganization(key, changes));
     dispatch(addGlobalSuccessMessage(translate('organization.updated')));
-  }, onFail(dispatch));
+  });
 };
 
-export const deleteOrganization = (key: string) => (dispatch: Dispatch<any>) => {
+export const deleteOrganization = (key: string) => (dispatch: Dispatch<Store>) => {
   return api.deleteOrganization(key).then(() => {
     dispatch(actions.deleteOrganization(key));
     dispatch(addGlobalSuccessMessage(translate('organization.deleted')));
-  }, onFail(dispatch));
+  });
 };
index 53941adef8ad55a279abbd5a48d36729f065a0be..e1b30839a3958f2c406827c89633a3834464c3d6 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { connect } from 'react-redux';
 import { RouterState } from 'react-router';
-import { getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer';
+import { getCurrentUser, getOrganizationByKey, Store } from '../../../store/rootReducer';
 import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization';
 import { Organization, CurrentUser, isLoggedIn } from '../../../app/types';
 
@@ -63,14 +63,12 @@ export class OrganizationAccess extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps) => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps) => ({
   currentUser: getCurrentUser(state),
   organization: getOrganizationByKey(state, ownProps.params.organizationKey)
 });
 
-const OrganizationAccessContainer = connect<StateToProps, {}, OwnProps>(mapStateToProps)(
-  OrganizationAccess
-);
+const OrganizationAccessContainer = connect(mapStateToProps)(OrganizationAccess);
 
 export function hasAdminAccess({
   currentUser,
index 08d359b25a8119804d55d7cb04c42408d4f55275..baadb3fd4cd8d91a65a5c6f82167f4b15ab02128 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { connect } from 'react-redux';
 import { RouterState } from 'react-router';
-import { getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer';
+import { getCurrentUser, getOrganizationByKey, Store } from '../../../store/rootReducer';
 import { Organization, CurrentUser } from '../../../app/types';
 
 interface StateToProps {
@@ -44,9 +44,9 @@ class OrganizationContainer extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps) => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps) => ({
   organization: getOrganizationByKey(state, ownProps.params.organizationKey),
   currentUser: getCurrentUser(state)
 });
 
-export default connect<StateToProps, {}, OwnProps>(mapStateToProps)(OrganizationContainer);
+export default connect(mapStateToProps)(OrganizationContainer);
index c1aac8ef5d68fa54af641fb9a14ab2e8bb854628..b3b7eb5711bb2db028c4d41bd622ec4d951e4a07 100644 (file)
@@ -128,7 +128,7 @@ export class OrganizationDelete extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps: DispatchToProps = { deleteOrganization: deleteOrganization as any };
 
-export default connect<null, DispatchToProps, OwnProps>(
+export default connect(
   null,
   mapDispatchToProps
 )(OrganizationDelete);
index a81cbe614d3b2c3f9abdbb042af33896bf2f8bc8..fa0bb5ca7f26e94d9ba0079dc1f3b847e9b1c1c4 100644 (file)
@@ -197,7 +197,7 @@ export class OrganizationEdit extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps = { updateOrganization: updateOrganization as any };
 
-export default connect<{}, DispatchProps, OwnProps>(
+export default connect(
   null,
   mapDispatchToProps
 )(OrganizationEdit);
index 41981090047c111da9e2842127f3cd25605b6f39..a4581693d1b0668a725c0a532f055ed1f7483718 100644 (file)
@@ -28,7 +28,8 @@ import { Organization, CurrentUser } from '../../../app/types';
 import {
   getOrganizationByKey,
   getCurrentUser,
-  getMyOrganizations
+  getMyOrganizations,
+  Store
 } from '../../../store/rootReducer';
 
 interface OwnProps {
@@ -110,7 +111,7 @@ export class OrganizationPage extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps) => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps) => ({
   currentUser: getCurrentUser(state),
   organization: getOrganizationByKey(state, ownProps.params.organizationKey),
   userOrganizations: getMyOrganizations(state)
@@ -118,7 +119,7 @@ const mapStateToProps = (state: any, ownProps: OwnProps) => ({
 
 const mapDispatchToProps = { fetchOrganization: fetchOrganization as any };
 
-export default connect<StateProps, DispatchToProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(OrganizationPage);
index 4ec2c2a21320314584aa056e9bf155ff77058a7f..4dabbca6786fccaaedf6897c159291509d17148b 100644 (file)
@@ -20,7 +20,7 @@
 import * as React from 'react';
 import { connect } from 'react-redux';
 import { withRouter, WithRouterProps } from 'react-router';
-import { areThereCustomOrganizations } from '../../store/rootReducer';
+import { areThereCustomOrganizations, Store } from '../../store/rootReducer';
 
 type ReactComponent<P> = React.ComponentClass<P> | React.StatelessComponent<P>;
 
@@ -44,7 +44,7 @@ export default function forSingleOrganization<P>(ComposedComponent: ReactCompone
     }
   }
 
-  const mapStateToProps = (state: any) => ({
+  const mapStateToProps = (state: Store) => ({
     customOrganizations: areThereCustomOrganizations(state)
   });
 
index 88b71f90e414770474a5a3802d2792f04e2da1e4..fb6b23e087b9a6a3dac6826f6c7c1101def63c28 100644 (file)
 import { connect } from 'react-redux';
 import OrganizationNavigationHeader from './OrganizationNavigationHeader';
 import { Organization } from '../../../app/types';
-import { getMyOrganizations } from '../../../store/rootReducer';
+import { getMyOrganizations, Store } from '../../../store/rootReducer';
 
 interface StateProps {
   organizations: Organization[];
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   organizations: getMyOrganizations(state)
 });
 
index 07ee53cd586b187fb8c6e52b86f126d5baad9b9c..518a2a86c52f63bce76e80d855701b1cba41fe93 100644 (file)
@@ -27,7 +27,7 @@ import NavBarTabs from '../../../components/nav/NavBarTabs';
 import { translate } from '../../../helpers/l10n';
 import { getQualityGatesUrl } from '../../../helpers/urls';
 import { hasPrivateAccess, isCurrentUserMemberOf } from '../../../helpers/organizations';
-import { getCurrentUser, getMyOrganizations } from '../../../store/rootReducer';
+import { getCurrentUser, getMyOrganizations, Store } from '../../../store/rootReducer';
 
 interface StateToProps {
   currentUser: CurrentUser;
@@ -99,9 +99,9 @@ export function OrganizationNavigationMenu({
   );
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state),
   userOrganizations: getMyOrganizations(state)
 });
 
-export default connect<StateToProps, {}, OwnProps>(mapStateToProps)(OrganizationNavigationMenu);
+export default connect(mapStateToProps)(OrganizationNavigationMenu);
index dc61e0ef7a8092fa8e058cbd5f99c7ca30070579..3f6982b46968045eaa89d1f5ade9a51495a7c0db 100644 (file)
@@ -47,7 +47,7 @@ import {
   isLongLivingBranch
 } from '../../../helpers/branches';
 import { fetchMetrics } from '../../../store/rootActions';
-import { getMetrics } from '../../../store/rootReducer';
+import { getMetrics, Store } from '../../../store/rootReducer';
 import { BranchLike, Component, Metric, MeasureEnhanced } from '../../../app/types';
 import { translate } from '../../../helpers/l10n';
 import '../styles.css';
@@ -262,11 +262,11 @@ export class OverviewApp extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps: DispatchToProps = { fetchMetrics };
 
-const mapStateToProps = (state: any): StateToProps => ({
+const mapStateToProps = (state: Store): StateToProps => ({
   metrics: getMetrics(state)
 });
 
-export default connect<StateToProps, DispatchToProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(OverviewApp);
index 53dace6236e2e17eecd86dd9263163eaafea8d22..5cf37a984c505935f5e41e734631924d7efa6cdf 100644 (file)
@@ -25,7 +25,7 @@ import MetaContainer from '../meta/MetaContainer';
 import { BranchLike, Component, CurrentUser, isLoggedIn } from '../../../app/types';
 import { isLongLivingBranch, isBranch, isMainBranch } from '../../../helpers/branches';
 import { translate } from '../../../helpers/l10n';
-import { getCurrentUser } from '../../../store/rootReducer';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
 import '../../../app/styles/sonarcloud.css';
 
 interface OwnProps {
@@ -119,8 +119,8 @@ export function WarningMessage({
   );
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state)
 });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(SonarCloudEmptyOverview);
+export default connect(mapStateToProps)(SonarCloudEmptyOverview);
index cd93c3a9fbd76892fea9884e0a02373bd87e1533..e6aba6d12c2df54f3ce7ee3a2c45d45f593d9221 100644 (file)
@@ -44,7 +44,8 @@ import { hasPrivateAccess } from '../../../helpers/organizations';
 import {
   getCurrentUser,
   getMyOrganizations,
-  getOrganizationByKey
+  getOrganizationByKey,
+  Store
 } from '../../../store/rootReducer';
 import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer';
 
@@ -170,10 +171,10 @@ export class Meta extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any, { component }: OwnProps) => ({
+const mapStateToProps = (state: Store, { component }: OwnProps) => ({
   currentUser: getCurrentUser(state),
   organization: getOrganizationByKey(state, component.organization),
   userOrganizations: getMyOrganizations(state)
 });
 
-export default connect<StateToProps, {}, OwnProps>(mapStateToProps)(Meta);
+export default connect(mapStateToProps)(Meta);
index a4628b8fa6e14f49c7dee56e1e74fd5cd7482ef6..eb8f8fc45f0b2a33f76244aaee4b848b05f04ff3 100644 (file)
@@ -25,7 +25,7 @@ import Tooltip from '../../../components/controls/Tooltip';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { getQualityProfileUrl } from '../../../helpers/urls';
 import { searchRules } from '../../../api/rules';
-import { getLanguages } from '../../../store/rootReducer';
+import { getLanguages, Store } from '../../../store/rootReducer';
 import { ComponentQualityProfile } from '../../../app/types';
 
 interface StateProps {
@@ -147,8 +147,8 @@ class MetaQualityProfiles extends React.PureComponent<StateProps & OwnProps, Sta
   }
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   languages: getLanguages(state)
 });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(MetaQualityProfiles);
+export default connect(mapStateToProps)(MetaQualityProfiles);
index 4fdc6e23ebf45d91b3d1b82ac68bfba2d37945ea..8ef1bc6c4a8bf86135554fc9dba148397b939308 100644 (file)
@@ -67,7 +67,7 @@ export default class MetaSize extends React.PureComponent<Props> {
     const className =
       this.props.component.qualifier === 'TRK' ? 'overview-meta-size-lang-dist' : 'big-spacer-top';
 
-    return languageDistribution ? (
+    return languageDistribution && languageDistribution.value !== undefined ? (
       <div className={className} id="overview-language-distribution">
         <LanguageDistributionContainer distribution={languageDistribution.value} width={160} />
       </div>
index 40067a021cb2a567952c7a38f5a220a782fff4c4..66203598154d8281f7cfafe504def45b52cb51ac 100644 (file)
@@ -17,6 +17,7 @@
  * 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 { connect } from 'react-redux';
 import AllHoldersList from './AllHoldersList';
 import {
@@ -34,35 +35,16 @@ import {
   getPermissionsAppGroups,
   getPermissionsAppQuery,
   getPermissionsAppFilter,
-  getPermissionsAppSelectedPermission
+  getPermissionsAppSelectedPermission,
+  Store
 } from '../../../../store/rootReducer';
 import { Organization } from '../../../../app/types';
-import { PermissionUser, PermissionGroup } from '../../../../api/permissions';
 
 interface OwnProps {
   organization?: Organization;
 }
 
-interface StateToProps {
-  filter: string;
-  groups: PermissionGroup[];
-  query: string;
-  selectedPermission?: string;
-  users: PermissionUser[];
-}
-
-interface DispatchToProps {
-  grantPermissionToGroup: (groupName: string, permission: string) => Promise<void>;
-  grantPermissionToUser: (login: string, permission: string) => Promise<void>;
-  loadHolders: () => void;
-  onFilter: (filter: string) => void;
-  onSearch: (query: string) => void;
-  onSelectPermission: (permission: string) => void;
-  revokePermissionFromGroup: (groupName: string, permission: string) => Promise<void>;
-  revokePermissionFromUser: (login: string, permission: string) => Promise<void>;
-}
-
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   filter: getPermissionsAppFilter(state),
   groups: getPermissionsAppGroups(state),
   query: getPermissionsAppQuery(state),
@@ -70,7 +52,7 @@ const mapStateToProps = (state: any) => ({
   users: getPermissionsAppUsers(state)
 });
 
-const mapDispatchToProps = (dispatch: Function, ownProps: OwnProps) => {
+const mapDispatchToProps = (dispatch: Dispatch<Store>, ownProps: OwnProps) => {
   const organizationKey = ownProps.organization ? ownProps.organization.key : undefined;
   return {
     grantPermissionToGroup: (groupName: string, permission: string) =>
@@ -89,7 +71,7 @@ const mapDispatchToProps = (dispatch: Function, ownProps: OwnProps) => {
   };
 };
 
-export default connect<StateToProps, DispatchToProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(AllHoldersList);
index c39d78e3d30e1018c5973a16eeb246b2cd614115..28f20e3ef740feaab84adaabc5327146d08f7162 100644 (file)
@@ -45,8 +45,7 @@ export type Props = {|
     qualifier: string,
     visibility: string
   },
-  onComponentChange: (changes: {}) => void,
-  onRequestFail: Object => void
+  onComponentChange: (changes: {}) => void
 |};
 */
 
@@ -208,7 +207,6 @@ export default class App extends React.PureComponent {
               loading: false,
               groups: this.removePermissionFromGroup(group, permission)
             });
-            this.props.onRequestFail(error);
           }
         });
     }
@@ -231,7 +229,6 @@ export default class App extends React.PureComponent {
               loading: false,
               users: this.removePermissionFromUser(user, permission)
             });
-            this.props.onRequestFail(error);
           }
         });
     }
@@ -254,7 +251,6 @@ export default class App extends React.PureComponent {
               loading: false,
               groups: this.addPermissionToGroup(group, permission)
             });
-            this.props.onRequestFail(error);
           }
         });
     }
@@ -277,7 +273,6 @@ export default class App extends React.PureComponent {
               loading: false,
               users: this.addPermissionToUser(user, permission)
             });
-            this.props.onRequestFail(error);
           }
         });
     }
index b95e406649f3ded597164bf2c45649c0a9ead4e8..2dcc1210a03e55d8edbe1a6ee33a384e1f8fe653 100644 (file)
  */
 import { connect } from 'react-redux';
 import App from './App';
-import { onFail } from '../../../../store/rootActions';
 import { getCurrentUser } from '../../../../store/rootReducer';
 
 const mapStateToProps = state => ({
   currentUser: getCurrentUser(state)
 });
 
-const mapDispatchToProps = dispatch => ({
-  onRequestFail: onFail(dispatch)
-});
-
-export default connect(
-  mapStateToProps,
-  mapDispatchToProps
-)(App);
+export default connect(mapStateToProps)(App);
index c28badc0fb3bdc3de33146bba9ddc829d8108c6d..cd2f3c0e89e65c15ec3034f131591caeefad68f6 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { connect } from 'react-redux';
-import { getPermissionsAppError } from '../../../../store/rootReducer';
+import { getPermissionsAppError, Store } from '../../../../store/rootReducer';
 
 interface Props {
   message: string;
@@ -33,7 +33,7 @@ function PageError({ message }: Props) {
   return <div className="alert alert-danger">{message}</div>;
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   message: getPermissionsAppError(state)
 });
 
index e12f6f7edc1f792086afe8c0f8ea20ed856127eb..b5bef871e77f60685f42f89396b91b1b2fd9130d 100644 (file)
@@ -33,7 +33,7 @@ import { getMeasures } from '../../../api/measures';
 import { getChildren } from '../../../api/components';
 import { translate } from '../../../helpers/l10n';
 import { fetchMetrics } from '../../../store/rootActions';
-import { getMetrics } from '../../../store/rootReducer';
+import { getMetrics, Store } from '../../../store/rootReducer';
 import { Metric, Component } from '../../../app/types';
 import '../styles.css';
 import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer';
@@ -220,11 +220,11 @@ export class App extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps: DispatchToProps = { fetchMetrics };
 
-const mapStateToProps = (state: any): StateToProps => ({
+const mapStateToProps = (state: Store): StateToProps => ({
   metrics: getMetrics(state)
 });
 
-export default connect<StateToProps, DispatchToProps, Props>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(App);
index e15edf3ad7cf16153c1ed9b0e7c706df3ad5dcfa..79558f42221eb4441756d82f0c9d27d24b4f1428 100644 (file)
@@ -22,10 +22,11 @@ import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessI
 import { ReportStatus, subscribe, unsubscribe } from '../../../api/report';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Button } from '../../../components/ui/buttons';
+import { CurrentUser, isLoggedIn } from '../../../app/types';
 
 interface Props {
   component: string;
-  currentUser: { email?: string };
+  currentUser: CurrentUser;
   status: ReportStatus;
 }
 
@@ -116,7 +117,7 @@ export default class Subscription extends React.PureComponent<Props, State> {
   );
 
   render() {
-    const hasEmail = !!this.props.currentUser.email;
+    const hasEmail = isLoggedIn(this.props.currentUser) && !!this.props.currentUser.email;
     const { subscribed } = this.state;
 
     let inner;
index d863f9494b6662ecd8597fe81341265d8afa8cf3..4076247a18f5b3d88f73af77c2f3db71ce53696c 100644 (file)
  */
 import { connect } from 'react-redux';
 import Subscription from './Subscription';
-import { getCurrentUser } from '../../../store/rootReducer';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state)
 });
 
-export default connect<any, any, any>(mapStateToProps)(Subscription);
+export default connect(mapStateToProps)(Subscription);
index da0539059305007972751df0f13afe3f78dace85..c5e364751bbbfa867882593c616d66789173daa0 100644 (file)
@@ -41,7 +41,7 @@ const status = {
   subscribed: true
 };
 
-const currentUser = { email: 'foo@example.com' };
+const currentUser = { isLoggedIn: true, email: 'foo@example.com' };
 
 beforeEach(() => {
   subscribe.mockClear();
@@ -68,7 +68,7 @@ it('renders when not subscribed', () => {
 
 it('renders when no email', () => {
   expect(
-    shallow(<Subscription component="foo" currentUser={{}} status={status} />)
+    shallow(<Subscription component="foo" currentUser={{ isLoggedIn: false }} status={status} />)
   ).toMatchSnapshot();
 });
 
index 9919f4b86046b7cd6b989b3a880e4d128137e0de..827c7872c366e8c6f0159962db4eede327af51e0 100644 (file)
@@ -30,7 +30,7 @@ import {
   addGlobalErrorMessage,
   addGlobalSuccessMessage,
   closeAllGlobalMessages
-} from '../../../store/globalMessages/duck';
+} from '../../../store/globalMessages';
 import RecentHistory from '../../../app/components/RecentHistory';
 
 class BulkUpdate extends React.PureComponent {
index 2d272a903ade2d681202abb9c1421c13d53614e1..b16f0f396d8c4329fd02ec72e5a0e08ad5a111f7 100644 (file)
@@ -32,7 +32,7 @@ import {
   addGlobalErrorMessage,
   addGlobalSuccessMessage,
   closeAllGlobalMessages
-} from '../../../store/globalMessages/duck';
+} from '../../../store/globalMessages';
 import RecentHistory from '../../../app/components/RecentHistory';
 import { getProjectAdminProjectModules } from '../../../store/rootReducer';
 
index b6ee8558bae46ad2d5f5a09f5647c3ded82a72db..6be51b994520bef657e6b81ffeb56ac9efb8934e 100644 (file)
  */
 import { connect } from 'react-redux';
 import App from './App';
-import { getAppState } from '../../../store/rootReducer';
+import { getAppState, Store } from '../../../store/rootReducer';
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   canAdmin: getAppState(state).canAdmin
 });
 
-export default connect<any, any, any>(mapStateToProps)(App);
+export default connect(mapStateToProps)(App);
index 2bf13b590b5148985e6c28078af0a576bb32523d..cb7a50672a7b61c24c8dd9d6d3a4772920693d34 100644 (file)
@@ -46,7 +46,7 @@ export interface Props {
   isFavorite: boolean;
   location: { pathname: string; query: RawQuery };
   organization: Organization | undefined;
-  organizationsEnabled: boolean;
+  organizationsEnabled?: boolean;
   storageOptionsSuffix?: string;
 }
 
index cf7600a82b63b60ecef1dead732cbb2dbe4ab548..178c281bd336fc061ee12b7adac1c491b1318c27 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { connect } from 'react-redux';
-import { CurrentUser, Organization } from '../../../app/types';
 import { lazyLoad } from '../../../components/lazyLoad';
-import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer';
-import { RawQuery } from '../../../helpers/query';
+import { getCurrentUser, areThereCustomOrganizations, Store } from '../../../store/rootReducer';
 
-interface StateProps {
-  currentUser: CurrentUser;
-  organizationsEnabled: boolean;
-}
-
-interface OwnProps {
-  isFavorite: boolean;
-  location: { pathname: string; query: RawQuery };
-  organization: Organization | undefined;
-  storageOptionsSuffix?: string;
-}
-
-const stateToProps = (state: any) => ({
+const stateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state),
   organizationsEnabled: areThereCustomOrganizations(state)
 });
 
-export default connect<StateProps, {}, OwnProps>(stateToProps)(
-  lazyLoad(() => import('./AllProjects'))
-);
+export default connect(stateToProps)(lazyLoad(() => import('./AllProjects')));
index f9cacdeec5386d0b38e45749bf96d7fadfe2dd03..f6a4309f37a4e16bc053722b4853f6d768fd59ff 100644 (file)
  */
 import { connect } from 'react-redux';
 import DefaultPageSelector from './DefaultPageSelector';
-import { CurrentUser } from '../../../app/types';
-import { getCurrentUser } from '../../../store/rootReducer';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
 
-interface StateProps {
-  currentUser: CurrentUser;
-}
-
-const stateToProps = (state: any) => ({
+const stateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state)
 });
 
-export default connect<StateProps>(stateToProps)(DefaultPageSelector);
+export default connect(stateToProps)(DefaultPageSelector);
index a869603b9ba17b390ad023e143cee6564d06fe11..b8f0a49b4a06fb27be402fae3b5a94579b35f1da 100644 (file)
@@ -19,9 +19,9 @@
  */
 import { connect } from 'react-redux';
 import FavoriteFilter from './FavoriteFilter';
-import { getCurrentUser } from '../../../store/rootReducer';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
 
-function mapStateToProps(state: any) {
+function mapStateToProps(state: Store) {
   return { currentUser: getCurrentUser(state) };
 }
 
index 271cf360e29ada8580452ab66eb2336d9a896101..e7d683038c3f78a808ea4c56d1d8b33b726db056 100644 (file)
@@ -26,7 +26,7 @@ import DropdownIcon from '../../../components/icons-components/DropdownIcon';
 import Dropdown from '../../../components/controls/Dropdown';
 import OrganizationListItem from '../../../components/ui/OrganizationListItem';
 import { Button } from '../../../components/ui/buttons';
-import { getMyOrganizations } from '../../../store/rootReducer';
+import { getMyOrganizations, Store } from '../../../store/rootReducer';
 import { isSonarCloud } from '../../../helpers/system';
 import { Organization } from '../../../app/types';
 import { translate } from '../../../helpers/l10n';
@@ -95,7 +95,7 @@ export class NoFavoriteProjects extends React.PureComponent<StateProps> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   organizations: getMyOrganizations(state)
 });
 
index 8d0815a9eb4144fc8b95b8db5e78d01b095eab08..1fd92c29037cd7e91f21c4f5a08e1d061def3edf 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { sortBy } from 'lodash';
 import Tooltip from '../../../components/controls/Tooltip';
 import { translate } from '../../../helpers/l10n';
-import { Languages } from '../../../store/languages/reducer';
+import { Languages } from '../../../store/languages';
 
 interface Props {
   distribution?: string;
index 9e67998c03ad581b6deb9fa8e72bdea3086433b5..31e30c841d3a3e4efe57f56fa5f615ac690e76b3 100644 (file)
  */
 import { connect } from 'react-redux';
 import ProjectCardLanguages from './ProjectCardLanguages';
-import { Languages } from '../../../store/languages/reducer';
-import { getLanguages } from '../../../store/rootReducer';
+import { getLanguages, Store } from '../../../store/rootReducer';
 
-interface StateProps {
-  languages: Languages;
-}
-
-const stateToProps = (state: any) => ({
+const stateToProps = (state: Store) => ({
   languages: getLanguages(state)
 });
 
-export default connect<StateProps>(stateToProps)(ProjectCardLanguages);
+export default connect(stateToProps)(ProjectCardLanguages);
index 3341a0a57fa90dc811e48969934b177fbcff7b80..32b1a71864dc4a07711879bb747a53c053ff3614 100644 (file)
@@ -22,7 +22,7 @@ import OrganizationLink from '../../../components/ui/OrganizationLink';
 
 interface Props {
   organization?: { key: string; name: string };
-  organizationsEnabled: boolean;
+  organizationsEnabled?: boolean;
 }
 
 export default function ProjectCardOrganization({ organization, organizationsEnabled }: Props) {
index da7c8856be7885c64aef4585b0751f483d854614..e637927e23d6681059334ad2709d77939a1e2bba 100644 (file)
  */
 import { connect } from 'react-redux';
 import ProjectCardOrganization from './ProjectCardOrganization';
-import { areThereCustomOrganizations } from '../../../store/rootReducer';
+import { areThereCustomOrganizations, Store } from '../../../store/rootReducer';
 
-interface StateProps {
-  organizationsEnabled: boolean;
-}
-
-const stateToProps = (state: any) => ({
+const stateToProps = (state: Store) => ({
   organizationsEnabled: areThereCustomOrganizations(state)
 });
 
-export default connect<StateProps>(stateToProps)(ProjectCardOrganization);
+export default connect(stateToProps)(ProjectCardOrganization);
index 41a518401c719d1cb786e4c0659c7fca9347b599..7e27c4accff2992b4773de293010b6e3605fe3ea 100644 (file)
@@ -28,9 +28,9 @@ import ManualProjectCreate from './ManualProjectCreate';
 import { serializeQuery, Query, parseQuery } from './utils';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
-import { getCurrentUser } from '../../../store/rootReducer';
-import { addGlobalErrorMessage } from '../../../store/globalMessages/duck';
-import { skipOnboarding as skipOnboardingAction } from '../../../store/users/actions';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
+import { addGlobalErrorMessage } from '../../../store/globalMessages';
+import { skipOnboarding as skipOnboardingAction } from '../../../store/users';
 import { CurrentUser, IdentityProvider, isLoggedIn, LoggedInUser } from '../../../app/types';
 import { skipOnboarding, getIdentityProviders } from '../../../api/users';
 import { translate } from '../../../helpers/l10n';
@@ -212,13 +212,13 @@ export class CreateProjectPage extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   currentUser: getCurrentUser(state)
 });
 
 const mapDispatchToProps: DispatchProps = { addGlobalErrorMessage, skipOnboardingAction };
 
-export default connect<StateProps, DispatchProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(CreateProjectPage);
index 299dc7676e0a81d7ef4c1122f9492dc9a64cb1d3..f2e63f0865028345c1e5e01501a82a37ec3bb4f4 100644 (file)
@@ -25,7 +25,7 @@ import Select from '../../../components/controls/Select';
 import { Button, SubmitButton } from '../../../components/ui/buttons';
 import { LoggedInUser, Organization } from '../../../app/types';
 import { fetchMyOrganizations } from '../../account/organizations/actions';
-import { getMyOrganizations } from '../../../store/rootReducer';
+import { getMyOrganizations, Store } from '../../../store/rootReducer';
 import { translate } from '../../../helpers/l10n';
 import { createProject } from '../../../api/components';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
@@ -217,12 +217,12 @@ const mapDispatchToProps = ({
   fetchMyOrganizations
 } as any) as DispatchProps;
 
-const mapStateToProps = (state: any): StateProps => {
+const mapStateToProps = (state: Store): StateProps => {
   return {
     userOrganizations: getMyOrganizations(state)
   };
 };
-export default connect<StateProps, DispatchProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(ManualProjectCreate);
index ada03f39f0aec34117d442d915e43e5591f44465..33229e381b7b531ab698c664762c9d8ab480b68d 100644 (file)
@@ -23,7 +23,7 @@ import Filter from './Filter';
 import FilterHeader from './FilterHeader';
 import SearchableFilterFooter from './SearchableFilterFooter';
 import SearchableFilterOption from './SearchableFilterOption';
-import { getLanguageByKey, Languages } from '../../../store/languages/reducer';
+import { getLanguageByKey, Languages } from '../../../store/languages';
 import { translate } from '../../../helpers/l10n';
 import { Facet } from '../types';
 import { RawQuery } from '../../../helpers/query';
index 43d9eb3d6b25ab67f9327a34bdf22d56da5a6792..2b5daab2df730e5595eccaf8cb96131cfc7a4ea6 100644 (file)
  */
 import { connect } from 'react-redux';
 import LanguagesFilter from './LanguagesFilter';
-import { Languages } from '../../../store/languages/reducer';
-import { getLanguages } from '../../../store/rootReducer';
+import { getLanguages, Store } from '../../../store/rootReducer';
 
-interface StateProps {
-  languages: Languages;
-}
-
-const stateToProps = (state: any) => ({
+const stateToProps = (state: Store) => ({
   languages: getLanguages(state)
 });
 
-export default connect<StateProps>(stateToProps)(LanguagesFilter);
+export default connect(stateToProps)(LanguagesFilter);
index 947b58fddde5757c13522f26ae424549a4b2148f..f5c8c5e612a5f994c72e40b28cfcb7eb108dd888 100644 (file)
@@ -29,7 +29,7 @@ import { Project } from '../types';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
 interface Props {
-  displayOrganizations: boolean;
+  displayOrganizations?: boolean;
   projects: Project[];
   sort?: string;
   total?: number;
index 30f543f096d10935f61c1236891424195b2f88f4..b7ac3c91a5a1d4730cbccb9dd8435a9c16a0de66 100644 (file)
@@ -22,8 +22,8 @@ import { connect } from 'react-redux';
 import App from './App';
 import forSingleOrganization from '../organizations/forSingleOrganization';
 import { Organization, LoggedInUser, Visibility } from '../../app/types';
-import { getAppState, getOrganizationByKey, getCurrentUser } from '../../store/rootReducer';
-import { receiveOrganizations } from '../../store/organizations/duck';
+import { getAppState, getOrganizationByKey, getCurrentUser, Store } from '../../store/rootReducer';
+import { receiveOrganizations } from '../../store/organizations';
 import { changeProjectDefaultVisibility } from '../../api/permissions';
 import { fetchOrganization } from '../organizations/actions';
 
@@ -40,6 +40,7 @@ interface DispatchProps {
 
 interface OwnProps {
   onRequestFail: (error: any) => void;
+  organization: Organization;
 }
 
 class AppContainer extends React.PureComponent<OwnProps & StateProps & DispatchProps> {
@@ -78,7 +79,7 @@ class AppContainer extends React.PureComponent<OwnProps & StateProps & DispatchP
   }
 }
 
-const mapStateToProps = (state: any, ownProps: any) => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps) => ({
   appState: getAppState(state),
   currentUser: getCurrentUser(state) as LoggedInUser,
   organization:
@@ -102,7 +103,7 @@ const mapDispatchToProps = (dispatch: Function) => ({
 });
 
 export default forSingleOrganization(
-  connect<StateProps, DispatchProps, OwnProps>(
+  connect(
     mapStateToProps,
     mapDispatchToProps
   )(AppContainer)
index d22d36f9c7ded8d8b7ecdb5a37029aa34975f1cf..d09415b9c644665d116426d4ae286cb19b761bab 100644 (file)
@@ -23,7 +23,7 @@ import Helmet from 'react-helmet';
 import { connect } from 'react-redux';
 import DetailsHeader from './DetailsHeader';
 import DetailsContent from './DetailsContent';
-import { getMetrics } from '../../../store/rootReducer';
+import { getMetrics, Store } from '../../../store/rootReducer';
 import { fetchMetrics } from '../../../store/rootActions';
 import { fetchQualityGate } from '../../../api/quality-gates';
 import { Metric, QualityGate, Condition } from '../../../app/types';
@@ -170,11 +170,11 @@ export class DetailsApp extends React.PureComponent<Props, State> {
 
 const mapDispatchToProps: DispatchToProps = { fetchMetrics };
 
-const mapStateToProps = (state: any): StateToProps => ({
+const mapStateToProps = (state: Store): StateToProps => ({
   metrics: getMetrics(state)
 });
 
-export default connect<StateToProps, DispatchToProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(DetailsApp);
index 67697a9c4f852ea61df4cbe7a2b857b9de5df1a4..5fa5abf66427ea4bb617887f6076d931ebc8068f 100644 (file)
@@ -24,13 +24,12 @@ import { sortProfiles } from '../utils';
 import { Exporter, Profile } from '../types';
 import OrganizationHelmet from '../../../components/common/OrganizationHelmet';
 import { translate } from '../../../helpers/l10n';
-import { Languages } from '../../../store/languages/reducer';
+import { Languages } from '../../../store/languages';
 import '../styles.css';
 
 interface Props {
   children: React.ReactElement<any>;
   languages: Languages;
-  onRequestFail: (reasong: any) => void;
   organization: { name: string; key: string } | undefined;
 }
 
@@ -103,7 +102,6 @@ export default class App extends React.PureComponent<Props, State> {
       languages: finalLanguages,
       exporters: this.state.exporters,
       updateProfiles: this.updateProfiles,
-      onRequestFail: this.props.onRequestFail,
       organization: organization ? organization.key : null
     });
   }
index 417c54699d6c9a0bb2588e7859f13344524cf582..525f5af615dfe33f328e7c692dacea2928c7a689 100644 (file)
 import { connect } from 'react-redux';
 import App from './App';
 import forSingleOrganization from '../../organizations/forSingleOrganization';
-import { getLanguages, getOrganizationByKey } from '../../../store/rootReducer';
-import { onFail } from '../../../store/rootActions';
-import { Languages } from '../../../store/languages/reducer';
+import { getLanguages, getOrganizationByKey, Store } from '../../../store/rootReducer';
 
-interface StateProps {
-  languages: Languages;
-  organization: { name: string; key: string } | undefined;
-}
-
-interface DispatchProps {
-  onRequestFail: (reasong: any) => void;
-}
-
-const mapStateToProps = (state: any, ownProps: any) => ({
+const mapStateToProps = (state: Store, ownProps: any) => ({
   languages: getLanguages(state),
   organization: ownProps.params.organizationKey
     ? getOrganizationByKey(state, ownProps.params.organizationKey)
     : undefined
 });
 
-const mapDispatchToProps = (dispatch: any) => ({
-  onRequestFail: (error: any) => onFail(dispatch)(error)
-});
-
-export default forSingleOrganization(
-  connect<StateProps, DispatchProps>(
-    mapStateToProps,
-    mapDispatchToProps
-  )(App)
-);
+export default forSingleOrganization(connect(mapStateToProps)(App));
index 39cd96b4c881e69366a3c2a67a584b0163d72bcd..d26262e3e47517c206123ae0afedd224680aa685 100644 (file)
@@ -27,7 +27,6 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
 interface Props {
   onClose: () => void;
   onCopy: (name: string) => void;
-  onRequestFail: (reasong: any) => void;
   profile: Profile;
 }
 
@@ -61,11 +60,10 @@ export default class CopyProfileForm extends React.PureComponent<Props, State> {
       this.setState({ loading: true });
       copyProfile(this.props.profile.key, name).then(
         (profile: any) => this.props.onCopy(profile.name),
-        (error: any) => {
+        () => {
           if (this.mounted) {
             this.setState({ loading: false });
           }
-          this.props.onRequestFail(error);
         }
       );
     }
index 39260d39eb73a33d4eb5f246eb5b637abd8217b8..941d82c311f37690698136936ae999e9479a3cf0 100644 (file)
@@ -27,7 +27,6 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
 interface Props {
   onClose: () => void;
   onDelete: () => void;
-  onRequestFail: (reason: any) => void;
   profile: Profile;
 }
 
@@ -51,11 +50,10 @@ export default class DeleteProfileForm extends React.PureComponent<Props, State>
   handleFormSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
     event.preventDefault();
     this.setState({ loading: true });
-    deleteProfile(this.props.profile.key).then(this.props.onDelete, (error: any) => {
+    deleteProfile(this.props.profile.key).then(this.props.onDelete, () => {
       if (this.mounted) {
         this.setState({ loading: false });
       }
-      this.props.onRequestFail(error);
     });
   };
 
index c4b3566803e1c4a3233086254388b3871a2e4d27..ac464d475c83d16b16c1027f50825ffdad12050a 100644 (file)
@@ -35,7 +35,6 @@ import ActionsDropdown, {
 interface Props {
   className?: string;
   fromList?: boolean;
-  onRequestFail: (reasong: any) => void;
   organization: string | null;
   profile: Profile;
   updateProfiles: () => Promise<void>;
@@ -198,7 +197,6 @@ export default class ProfileActions extends React.PureComponent<Props, State> {
           <CopyProfileForm
             onClose={this.closeCopyForm}
             onCopy={this.handleProfileCopy}
-            onRequestFail={this.props.onRequestFail}
             profile={profile}
           />
         )}
@@ -207,7 +205,6 @@ export default class ProfileActions extends React.PureComponent<Props, State> {
           <DeleteProfileForm
             onClose={this.closeDeleteForm}
             onDelete={this.handleProfileDelete}
-            onRequestFail={this.props.onRequestFail}
             profile={profile}
           />
         )}
@@ -216,7 +213,6 @@ export default class ProfileActions extends React.PureComponent<Props, State> {
           <RenameProfileForm
             onClose={this.closeRenameForm}
             onRename={this.handleProfileRename}
-            onRequestFail={this.props.onRequestFail}
             profile={profile}
           />
         )}
index 40294051be51339f778d0c0d89fa5612a6cd89ea..f8f1fdfacad05d4932a6615da3ce901465b40249 100644 (file)
@@ -30,7 +30,6 @@ interface Props {
     pathname: string;
     query: { key?: string; language: string; name: string };
   };
-  onRequestFail: (reasong: any) => void;
   organization: string | null;
   profiles: Profile[];
   updateProfiles: () => Promise<void>;
@@ -75,7 +74,6 @@ export default class ProfileContainer extends React.PureComponent<Props & WithRo
     }
 
     const child = React.cloneElement(this.props.children, {
-      onRequestFail: this.props.onRequestFail,
       organization,
       profile,
       profiles,
@@ -86,7 +84,6 @@ export default class ProfileContainer extends React.PureComponent<Props & WithRo
       <div id="quality-profile">
         <Helmet title={profile.name} />
         <ProfileHeader
-          onRequestFail={this.props.onRequestFail}
           organization={organization}
           profile={profile}
           updateProfiles={this.props.updateProfiles}
index fd04227ede9b1201aefa07914f425fa5e6ec9db8..f8744737d2d82cc045657dfabd56f688b1d0f7ab 100644 (file)
@@ -27,7 +27,6 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
 interface Props {
   onClose: () => void;
   onRename: (name: string) => void;
-  onRequestFail: (reason: any) => void;
   profile: Profile;
 }
 
@@ -61,11 +60,10 @@ export default class RenameProfileForm extends React.PureComponent<Props, State>
       this.setState({ loading: true });
       renameProfile(this.props.profile.key, name).then(
         () => this.props.onRename(name),
-        (error: any) => {
+        () => {
           if (this.mounted) {
             this.setState({ loading: false });
           }
-          this.props.onRequestFail(error);
         }
       );
     }
index c407c0e64fe6bc70260092cd47d4d4566a45d243..6e12e93f278dd552ea138c7578596b0d3e1b5f37 100644 (file)
@@ -40,14 +40,7 @@ const PROFILE = {
 
 it('renders with no permissions', () => {
   expect(
-    shallow(
-      <ProfileActions
-        onRequestFail={jest.fn()}
-        organization="org"
-        profile={PROFILE}
-        updateProfiles={jest.fn()}
-      />
-    )
+    shallow(<ProfileActions organization="org" profile={PROFILE} updateProfiles={jest.fn()} />)
   ).toMatchSnapshot();
 });
 
@@ -55,7 +48,6 @@ it('renders with permission to edit only', () => {
   expect(
     shallow(
       <ProfileActions
-        onRequestFail={jest.fn()}
         organization="org"
         profile={{ ...PROFILE, actions: { edit: true } }}
         updateProfiles={jest.fn()}
@@ -68,7 +60,6 @@ it('renders with all permissions', () => {
   expect(
     shallow(
       <ProfileActions
-        onRequestFail={jest.fn()}
         organization="org"
         profile={{
           ...PROFILE,
@@ -91,7 +82,6 @@ it('should copy profile', async () => {
   const push = jest.fn();
   const wrapper = shallow(
     <ProfileActions
-      onRequestFail={jest.fn()}
       organization="org"
       profile={{ ...PROFILE, actions: { copy: true } }}
       updateProfiles={updateProfiles}
index 756b21b6b316fa9abf943c0a0860f9713ccb279a..c04577b217bf2a95de6e375e8e04552211cb992a 100644 (file)
@@ -35,7 +35,6 @@ it('should render ProfileHeader', () => {
   const output = shallow(
     <ProfileContainer
       location={{ pathname: '', query: { language: 'js', name: 'fake' } }}
-      onRequestFail={jest.fn()}
       organization={null}
       profiles={profiles}
       updateProfiles={updateProfiles}
@@ -57,7 +56,6 @@ it('should render ProfileNotFound', () => {
   const output = shallow(
     <ProfileContainer
       location={{ pathname: '', query: { language: 'js', name: 'random' } }}
-      onRequestFail={jest.fn()}
       organization={null}
       profiles={profiles}
       updateProfiles={jest.fn()}
@@ -74,7 +72,6 @@ it('should render Helmet', () => {
   const output = shallow(
     <ProfileContainer
       location={{ pathname: '', query: { language: 'js', name: 'First Profile' } }}
-      onRequestFail={jest.fn()}
       organization={null}
       profiles={profiles}
       updateProfiles={updateProfiles}
index b783eb7e411039b01c82849af21961c319fa0b8f..af740ec35a4e2306de238ed7481d941b976ac032 100644 (file)
@@ -29,7 +29,6 @@ import { translate } from '../../../helpers/l10n';
 interface Props {
   onChange: () => void;
   onClose: () => void;
-  onRequestFail: (reasong: any) => void;
   profile: Profile;
   profiles: Profile[];
 }
@@ -67,11 +66,10 @@ export default class ChangeParentForm extends React.PureComponent<Props, State>
       this.setState({ loading: true });
       changeProfileParent(this.props.profile.key, parent)
         .then(this.props.onChange)
-        .catch((error: any) => {
+        .catch(() => {
           if (this.mounted) {
             this.setState({ loading: false });
           }
-          this.props.onRequestFail(error);
         });
     }
   };
index 62cc3952052360738138410d97805cde0879c5f0..7fa67e0b743625e717ac588b76a71f2568454331 100644 (file)
@@ -27,7 +27,6 @@ import { Exporter, Profile } from '../types';
 
 interface Props {
   exporters: Exporter[];
-  onRequestFail: (reasong: any) => void;
   organization: string | null;
   profile: Profile;
   profiles: Profile[];
index 2315bfcb57b2d06126c8ece962be02dcf67ac018..06dfba05ee260c33fb09a88414f89e115330c297 100644 (file)
@@ -33,7 +33,6 @@ import {
 import { Profile } from '../types';
 
 interface Props {
-  onRequestFail: (reasong: any) => void;
   profile: Profile;
   organization: string | null;
   updateProfiles: () => Promise<void>;
@@ -111,7 +110,6 @@ export default class ProfileHeader extends React.PureComponent<Props> {
             <li>
               <ProfileActions
                 className="pull-left"
-                onRequestFail={this.props.onRequestFail}
                 organization={organization}
                 profile={profile}
                 updateProfiles={this.props.updateProfiles}
index 85deb056f70c02cb94f456e84d0ac37b78de8b86..811d248495cb6dd8c5583ce4019c54472fcbf786 100644 (file)
@@ -27,7 +27,6 @@ import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
 interface Props {
-  onRequestFail: (reason: any) => void;
   organization: string | null;
   profile: Profile;
   profiles: Profile[];
@@ -192,7 +191,6 @@ export default class ProfileInheritance extends React.PureComponent<Props, State
           <ChangeParentForm
             onChange={this.handleParentChange}
             onClose={this.closeForm}
-            onRequestFail={this.props.onRequestFail}
             profile={profile}
             profiles={profiles.filter(p => p !== profile && p.language === profile.language)}
           />
index ed4b88c7c3461a1e77c84ff7dcf6c98458cc06c4..38d2cb8e0d7130370b11a9cd9b2ee915b394b2c5 100644 (file)
@@ -27,7 +27,6 @@ it('renders without permissions', () => {
     shallow(
       <ProfileDetails
         exporters={[]}
-        onRequestFail={jest.fn()}
         organization="org"
         profile={{} as Profile}
         profiles={[]}
@@ -42,7 +41,6 @@ it('renders with edit permission', () => {
     shallow(
       <ProfileDetails
         exporters={[]}
-        onRequestFail={jest.fn()}
         organization="org"
         profile={{ actions: { edit: true } } as Profile}
         profiles={[]}
index 57001b3e68d59bbd9755f79cd4fe02fa0d066351..9f37d14f9e268bab58ee83fe29eec170621a7139 100644 (file)
@@ -10,7 +10,6 @@ exports[`renders with edit permission 1`] = `
     >
       <ProfileRules
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={
           Object {
@@ -24,7 +23,6 @@ exports[`renders with edit permission 1`] = `
       />
       <ProfileExporters
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={
           Object {
@@ -52,7 +50,6 @@ exports[`renders with edit permission 1`] = `
     >
       <ProfileInheritance
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={
           Object {
@@ -66,7 +63,6 @@ exports[`renders with edit permission 1`] = `
       />
       <ProfileProjects
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={
           Object {
@@ -93,7 +89,6 @@ exports[`renders without permissions 1`] = `
     >
       <ProfileRules
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={Object {}}
         profiles={Array []}
@@ -101,7 +96,6 @@ exports[`renders without permissions 1`] = `
       />
       <ProfileExporters
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={Object {}}
         profiles={Array []}
@@ -113,7 +107,6 @@ exports[`renders without permissions 1`] = `
     >
       <ProfileInheritance
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={Object {}}
         profiles={Array []}
@@ -121,7 +114,6 @@ exports[`renders without permissions 1`] = `
       />
       <ProfileProjects
         exporters={Array []}
-        onRequestFail={[MockFunction]}
         organization="org"
         profile={Object {}}
         profiles={Array []}
index e9fcc95b3ad9a22639bfee8d4a3ff6bd06113186..388675ba4d44039f43a92ff568b725e2bef7a2eb 100644 (file)
@@ -29,7 +29,6 @@ interface Props {
   languages: Array<{ key: string; name: string }>;
   onClose: () => void;
   onCreate: Function;
-  onRequestFail: (reasong: any) => void;
   organization: string | null;
 }
 
@@ -89,11 +88,10 @@ export default class CreateProfileForm extends React.PureComponent<Props, State>
 
     createQualityProfile(data).then(
       (response: any) => this.props.onCreate(response.profile),
-      (error: any) => {
+      () => {
         if (this.mounted) {
           this.setState({ loading: false });
         }
-        this.props.onRequestFail(error);
       }
     );
   };
index af78f87f44e691fc77e03f4a29a5c28be80d03ae..201eedb516497e5ac5f7b04d209d852be2feef74 100644 (file)
@@ -28,7 +28,6 @@ interface Props {
   actions: Actions;
   languages: Array<{ key: string; name: string }>;
   location: { query: { [p: string]: string } };
-  onRequestFail: (reason: any) => void;
   organization: string | null;
   profiles: Profile[];
   updateProfiles: () => Promise<void>;
index 4bfb1c02c774e85f283ed20ccbd77860a143724a..d064cfb708cb3e44576cb7c3622fcbd0b1dd73fa 100644 (file)
@@ -31,7 +31,6 @@ import { translate } from '../../../helpers/l10n';
 interface Props {
   actions: Actions;
   languages: Array<{ key: string; name: string }>;
-  onRequestFail: (reason: any) => void;
   organization: string | null;
   updateProfiles: () => Promise<void>;
 }
@@ -112,7 +111,6 @@ export default class PageHeader extends React.PureComponent<Props, State> {
         {this.state.restoreFormOpen && (
           <RestoreProfileForm
             onClose={this.closeRestoreForm}
-            onRequestFail={this.props.onRequestFail}
             onRestore={this.props.updateProfiles}
             organization={this.props.organization}
           />
@@ -123,7 +121,6 @@ export default class PageHeader extends React.PureComponent<Props, State> {
             languages={this.props.languages}
             onClose={this.closeCreateForm}
             onCreate={this.handleCreate}
-            onRequestFail={this.props.onRequestFail}
             organization={this.props.organization}
           />
         )}
index 05faaa8ef8f5ec00411927b9607e3b77a665cb32..599c0f398d83205a80c72bf13dc65d7b8f1710f5 100644 (file)
@@ -28,7 +28,6 @@ import { Profile } from '../types';
 interface Props {
   languages: Array<{ key: string; name: string }>;
   location: { query: { [p: string]: string } };
-  onRequestFail: (reason: any) => void;
   organization: string | null;
   profiles: Profile[];
   updateProfiles: () => Promise<void>;
@@ -39,7 +38,6 @@ export default class ProfilesList extends React.PureComponent<Props> {
     return profiles.map(profile => (
       <ProfilesListRow
         key={profile.key}
-        onRequestFail={this.props.onRequestFail}
         organization={this.props.organization}
         profile={profile}
         updateProfiles={this.props.updateProfiles}
index 5fb2c5e1fac82100cd15d35c332a31f0bfc6696a..682ede61e62ef1f700dccdf6ee764b0f3183082d 100644 (file)
@@ -31,7 +31,6 @@ import Tooltip from '../../../components/controls/Tooltip';
 import DocTooltip from '../../../components/docs/DocTooltip';
 
 interface Props {
-  onRequestFail: (reason: any) => void;
   organization: string | null;
   profile: Profile;
   updateProfiles: () => Promise<void>;
@@ -149,7 +148,6 @@ export default class ProfilesListRow extends React.PureComponent<Props> {
         <td className="quality-profiles-table-actions thin nowrap text-middle text-right">
           <ProfileActions
             fromList={true}
-            onRequestFail={this.props.onRequestFail}
             organization={this.props.organization}
             profile={this.props.profile}
             updateProfiles={this.props.updateProfiles}
index a93f0e0a44ebc9487bb5a4cd493a9198852462a9..ce22a7a15fa670ed05017e9768b5a6b42652d607 100644 (file)
@@ -25,7 +25,6 @@ import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';
 
 interface Props {
   onClose: () => void;
-  onRequestFail: (reason: any) => void;
   onRestore: () => void;
   organization: string | null;
 }
@@ -71,11 +70,10 @@ export default class RestoreProfileForm extends React.PureComponent<Props, State
         }
         this.props.onRestore();
       },
-      (error: any) => {
+      () => {
         if (this.mounted) {
           this.setState({ loading: false });
         }
-        this.props.onRequestFail(error);
       }
     );
   };
index ed4c7dacfe9c284e1e2b032c914002ac955b4ef4..a51599f362c3ce4ffd4ed9249681aa414dc8c72c 100644 (file)
@@ -116,7 +116,7 @@ class LoginContainer extends React.PureComponent<Props, State> {
 const mapStateToProps = null;
 const mapDispatchToProps = { doLogin: doLogin as any };
 
-export default connect<{}, DispatchToProps, OwnProps>(
+export default connect(
   mapStateToProps,
   mapDispatchToProps
 )(LoginContainer);
index d4d1dddd806979f69dfc06081fc54939c48c3008..34bdf549004a8a6195edab04f45d689439b5aa3b 100644 (file)
@@ -29,7 +29,7 @@ import {
   resetSettingValue
 } from '../../../api/settings';
 import { parseError } from '../../../helpers/request';
-import { addGlobalErrorMessage, closeAllGlobalMessages } from '../../../store/globalMessages/duck';
+import { addGlobalErrorMessage, closeAllGlobalMessages } from '../../../store/globalMessages';
 import { isEmptyValue } from '../utils';
 import { translate } from '../../../helpers/l10n';
 import { getSettingsAppDefinition, getSettingsAppChangedValue } from '../../../store/rootReducer';
index 55ce9d6756d233e19143e75895386310441025cc..f20e9e91c9060d46a8e2df2e09dda42244d83d02 100644 (file)
@@ -22,7 +22,7 @@ import { parseError } from '../../../../helpers/request';
 import {
   addGlobalErrorMessage,
   closeAllGlobalMessages
-} from '../../../../store/globalMessages/duck';
+} from '../../../../store/globalMessages';
 
 export const UPDATE_ENCRYPTION = 'UPDATE_ENCRYPTION';
 
index 13320969836e8ff7cc31649ea417899c0e5febc3..37f7f1ecd563cb547d697dd2392791228be3707e 100644 (file)
@@ -23,8 +23,8 @@ import definitions, * as fromDefinitions from './definitions/reducer';
 import encryptionPage from './encryptionPage/reducer';
 import values, * as fromValues from './values/reducer';
 import settingsPage, * as fromSettingsPage from './settingsPage/reducer';
-import globalMessages, * as fromGlobalMessages from '../../../store/globalMessages/duck';
-/*:: import type { State as GlobalMessagesState } from '../../../store/globalMessages/duck'; */
+import globalMessages, * as fromGlobalMessages from '../../../store/globalMessages';
+/*:: import type { State as GlobalMessagesState } from '../../../store/globalMessages'; */
 /*:: import type { State as ValuesState } from './values/reducer'; */
 
 /*::
index b8006ff37584690a014f38164acde0440c698821..1ed6b84045e3d3511bec70025530fc7545fd9212 100644 (file)
@@ -27,7 +27,7 @@ import OnboardingTeamIcon from '../../../components/icons-components/OnboardingT
 import { Button, ResetButtonLink } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 import { CurrentUser, isLoggedIn } from '../../../app/types';
-import { getCurrentUser } from '../../../store/rootReducer';
+import { getCurrentUser, Store } from '../../../store/rootReducer';
 import '../styles.css';
 
 interface OwnProps {
@@ -94,6 +94,6 @@ export class OnboardingModal extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({ currentUser: getCurrentUser(state) });
+const mapStateToProps = (state: Store): StateProps => ({ currentUser: getCurrentUser(state) });
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(OnboardingModal);
+export default connect(mapStateToProps)(OnboardingModal);
index cd5bc33f08cdc0a008804613a02c15f9806ace0c..2d6901b63c9207c6022174b637db5591ff9d8d22 100644 (file)
@@ -22,7 +22,7 @@ import * as PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import OnboardingModal from './OnboardingModal';
 import { skipOnboarding } from '../../../api/users';
-import { skipOnboarding as skipOnboardingAction } from '../../../store/users/actions';
+import { skipOnboarding as skipOnboardingAction } from '../../../store/users';
 import CreateOrganizationForm from '../../account/organizations/CreateOrganizationForm';
 import TeamOnboardingModal from '../teamOnboarding/TeamOnboardingModal';
 import { Organization } from '../../../app/types';
@@ -96,7 +96,7 @@ export class OnboardingPage extends React.PureComponent<DispatchProps, State> {
 
 const mapDispatchToProps: DispatchProps = { skipOnboardingAction };
 
-export default connect<{}, DispatchProps>(
+export default connect(
   null,
   mapDispatchToProps
 )(OnboardingPage);
index e3511631aa473e8bd1d55b189df1db64d5d0be91..9068fb81f94a98f511ffe6f3ec0386dc85d54e33 100644 (file)
@@ -26,7 +26,7 @@ import ProjectAnalysisStep from '../components/ProjectAnalysisStep';
 import OrganizationStep from '../components/OrganizationStep';
 import TokenStep from '../components/TokenStep';
 import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
-import { getCurrentUser, areThereCustomOrganizations } from '../../../store/rootReducer';
+import { getCurrentUser, areThereCustomOrganizations, Store } from '../../../store/rootReducer';
 import { CurrentUser, isLoggedIn } from '../../../app/types';
 import { ResetButtonLink } from '../../../components/ui/buttons';
 import { getProjectUrl } from '../../../helpers/urls';
@@ -41,7 +41,7 @@ interface OwnProps {
 
 interface StateProps {
   currentUser: CurrentUser;
-  organizationsEnabled: boolean;
+  organizationsEnabled?: boolean;
 }
 
 type Props = OwnProps & StateProps;
@@ -196,11 +196,11 @@ export class ProjectOnboarding extends React.PureComponent<Props, State> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => {
+const mapStateToProps = (state: Store): StateProps => {
   return {
     currentUser: getCurrentUser(state),
     organizationsEnabled: areThereCustomOrganizations(state)
   };
 };
 
-export default connect<StateProps, {}, OwnProps>(mapStateToProps)(ProjectOnboarding);
+export default connect(mapStateToProps)(ProjectOnboarding);
index e3e747805fc56b3c60c8e2f5c9bb7ab7fd22ceb2..d22ab7bcf52fd70eddce803d2354f50f9d83842f 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import * as PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import ProjectOnboardingModal from './ProjectOnboardingModal';
-import { skipOnboarding } from '../../../store/users/actions';
+import { skipOnboarding } from '../../../store/users';
 
 interface DispatchProps {
   skipOnboarding: () => void;
@@ -44,7 +44,7 @@ export class ProjectOnboardingPage extends React.PureComponent<DispatchProps> {
 
 const mapDispatchToProps: DispatchProps = { skipOnboarding };
 
-export default connect<{}, DispatchProps>(
+export default connect(
   null,
   mapDispatchToProps
 )(ProjectOnboardingPage);
index edefebb10867d0038b1f5fd5b7462fda29a2937f..9c898e36ad66f1cf43f7503213981efc7dc605bf 100644 (file)
@@ -34,7 +34,7 @@ import { translate } from '../../helpers/l10n';
 interface Props {
   currentUser: { isLoggedIn: boolean; login?: string };
   location: Location;
-  organizationsEnabled: boolean;
+  organizationsEnabled?: boolean;
 }
 
 interface State {
index 47fdd0afb3a4b61ce2b6a0614dc279c02931af6f..563ed0cfd2102a6d6370604baaf7d9c52970f512 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { connect } from 'react-redux';
-import { Location } from 'history';
 import UsersApp from './UsersApp';
-import { areThereCustomOrganizations, getCurrentUser } from '../../store/rootReducer';
+import { areThereCustomOrganizations, getCurrentUser, Store } from '../../store/rootReducer';
 
-interface OwnProps {
-  location: Location;
-}
-
-interface StateToProps {
-  currentUser: { isLoggedIn: boolean; login?: string };
-  organizationsEnabled: boolean;
-}
-
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   currentUser: getCurrentUser(state),
   organizationsEnabled: areThereCustomOrganizations(state)
 });
 
-export default connect<StateToProps, {}, OwnProps>(mapStateToProps)(UsersApp);
+export default connect(mapStateToProps)(UsersApp);
index 8888af81c5104683cdecf684a308491d0aebecdd..cb91c04a60a150d0a8aa3cc155adb943baf573d6 100644 (file)
@@ -26,7 +26,7 @@ interface Props {
   currentUser: { isLoggedIn: boolean; login?: string };
   identityProviders: IdentityProvider[];
   onUpdateUsers: () => void;
-  organizationsEnabled: boolean;
+  organizationsEnabled?: boolean;
   updateTokensCount: (login: string, tokensCount: number) => void;
   users: User[];
 }
index 335628489896b6ac67c57148b6c2d45b149d9b48..10d34f4a158e44926d4c113bff95e036afc637b8 100644 (file)
@@ -33,7 +33,7 @@ interface Props {
   identityProvider?: IdentityProvider;
   isCurrentUser: boolean;
   onUpdateUsers: () => void;
-  organizationsEnabled: boolean;
+  organizationsEnabled?: boolean;
   updateTokensCount: (login: string, tokensCount: number) => void;
   user: User;
 }
index 457bc24c4eae0847452d0149e4e03ea66b34595c..9c4fbe34a9ab5d79cfa699efcf926ba268b5d74a 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import { find, sortBy } from 'lodash';
+import { sortBy } from 'lodash';
 import Histogram from './Histogram';
 import { formatMeasure } from '../../helpers/measures';
-import { Language } from '../../api/languages';
 import { translate } from '../../helpers/l10n';
+import { Languages } from '../../store/languages';
 
 interface Props {
   alignTicks?: boolean;
   distribution: string;
-  languages?: Language[];
+  languages: Languages;
   width: number;
 }
 
@@ -61,7 +61,7 @@ export default function LanguageDistribution(props: Props) {
     if (langKey === '<null>') {
       return translate('unknown');
     }
-    const lang = find(props.languages, { key: langKey });
+    const lang = props.languages[langKey];
     return lang ? lang.name : langKey;
   }
 }
index 9ef2039202e660d9bec1d9bcc2c1ad27d9cb975e..abbf2a645b82574656190cdcb3b4569ca55cfa8d 100644 (file)
  */
 import { connect } from 'react-redux';
 import LanguageDistribution from './LanguageDistribution';
-import { getLanguages } from '../../store/rootReducer';
+import { getLanguages, Store } from '../../store/rootReducer';
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   languages: getLanguages(state)
 });
 
-export default connect<any, any, any>(mapStateToProps)(LanguageDistribution);
+export default connect(mapStateToProps)(LanguageDistribution);
index 224244ca51ca50ff0977f2bb419dcdf80bb7fc1a..bf544029f259afdbaf500f80490629b532280fb7 100644 (file)
@@ -26,7 +26,7 @@ it('renders', () => {
     shallow(
       <LanguageDistribution
         distribution="java=1734;js=845;cpp=73;<null>=15"
-        languages={[{ key: 'java', name: 'Java' }, { key: 'js', name: 'JavaScript' }]}
+        languages={{ java: { key: 'java', name: 'Java' }, js: { key: 'js', name: 'JavaScript' } }}
         width={100}
       />
     )
index 2c702af7c02461263af1f6b51b2ce20e1138df2d..1a72a92c3548d08d9557d30e295a331db4be9929 100644 (file)
@@ -26,7 +26,12 @@ import { translate } from '../../helpers/l10n';
 import { Visibility, Organization, CurrentUser } from '../../app/types';
 import { isSonarCloud } from '../../helpers/system';
 import { isCurrentUserMemberOf, isPaidOrganization } from '../../helpers/organizations';
-import { getCurrentUser, getOrganizationByKey, getMyOrganizations } from '../../store/rootReducer';
+import {
+  getCurrentUser,
+  getOrganizationByKey,
+  getMyOrganizations,
+  Store
+} from '../../store/rootReducer';
 import VisibleIcon from '../icons-components/VisibleIcon';
 import DocTooltip from '../docs/DocTooltip';
 
@@ -99,7 +104,7 @@ export function PrivacyBadge({
   );
 }
 
-const mapStateToProps = (state: any, { organization }: OwnProps) => {
+const mapStateToProps = (state: Store, { organization }: OwnProps) => {
   if (typeof organization === 'string') {
     organization = getOrganizationByKey(state, organization);
   }
@@ -110,7 +115,7 @@ const mapStateToProps = (state: any, { organization }: OwnProps) => {
   };
 };
 
-export default connect<StateToProps, {}, OwnProps>(mapStateToProps)(PrivacyBadge);
+export default connect(mapStateToProps)(PrivacyBadge);
 
 function getDoc(visibility: Visibility, icon: JSX.Element | null, organization: Organization) {
   let doc;
index d71cec022b5373736337d34c4b0df77172f8ae29..5be1bbc5816724ac1c3fb7aa7505a4a29a697d32 100644 (file)
@@ -24,8 +24,8 @@ import Tooltip from './Tooltip';
 import HomeIcon from '../icons-components/HomeIcon';
 import { CurrentUser, isLoggedIn, HomePage, isSameHomePage } from '../../app/types';
 import { translate } from '../../helpers/l10n';
-import { getCurrentUser } from '../../store/rootReducer';
-import { setHomePage } from '../../store/users/actions';
+import { getCurrentUser, Store } from '../../store/rootReducer';
+import { setHomePage } from '../../store/users';
 
 interface StateProps {
   currentUser: CurrentUser;
@@ -81,7 +81,7 @@ class HomePageSelect extends React.PureComponent<Props> {
   }
 }
 
-const mapStateToProps = (state: any): StateProps => ({
+const mapStateToProps = (state: Store): StateProps => ({
   currentUser: getCurrentUser(state)
 });
 
index 4d5af634c914c744941182155c2d859f6955ea2b..89803a78e9768b916b2c4793aebb80e251a49c5b 100644 (file)
@@ -21,9 +21,9 @@ import * as React from 'react';
 import { shallow } from 'enzyme';
 import HomePageSelect from '../HomePageSelect';
 import { setHomePage } from '../../../api/users';
-import { HomePageType, HomePage, LoggedInUser } from '../../../app/types';
+import { HomePageType, HomePage, LoggedInUser, CurrentUser } from '../../../app/types';
 import { click } from '../../../helpers/testUtils';
-import rootReducer, { getCurrentUser } from '../../../store/rootReducer';
+import rootReducer, { getCurrentUser, Store } from '../../../store/rootReducer';
 import configureStore from '../../../store/utils/configureStore';
 
 jest.mock('../../../api/users', () => ({
@@ -33,29 +33,35 @@ jest.mock('../../../api/users', () => ({
 const homepage: HomePage = { type: HomePageType.Projects };
 
 it('should render unchecked', () => {
-  const store = configureStore(rootReducer, { users: { currentUser: { isLoggedIn: true } } });
+  const store = configureStore(rootReducer, {
+    users: { currentUser: { isLoggedIn: true } }
+  } as Store);
   expect(getWrapper(homepage, store)).toMatchSnapshot();
 });
 
 it('should render checked', () => {
   const store = configureStore(rootReducer, {
-    users: { currentUser: { isLoggedIn: true, homepage } }
-  });
+    users: { currentUser: { isLoggedIn: true, homepage } as CurrentUser }
+  } as Store);
   expect(getWrapper(homepage, store)).toMatchSnapshot();
 });
 
 it('should set new home page', async () => {
-  const store = configureStore(rootReducer, { users: { currentUser: { isLoggedIn: true } } });
+  const store = configureStore(rootReducer, {
+    users: { currentUser: { isLoggedIn: true } }
+  } as Store);
   const wrapper = getWrapper(homepage, store);
   click(wrapper.find('a'));
   await new Promise(setImmediate);
-  const currentUser = getCurrentUser(store.getState()) as LoggedInUser;
+  const currentUser = getCurrentUser(store.getState() as Store) as LoggedInUser;
   expect(currentUser.homepage).toEqual(homepage);
   expect(setHomePage).toBeCalledWith(homepage);
 });
 
 it('should not render for anonymous', () => {
-  const store = configureStore(rootReducer, { users: { currentUser: { isLoggedIn: false } } });
+  const store = configureStore(rootReducer, {
+    users: { currentUser: { isLoggedIn: false } }
+  } as Store);
   expect(getWrapper(homepage, store).type()).toBeNull();
 });
 
index 0ea3dedee6e07db59122d9c66370ac344aded2ab..3562a3c6baded1eaf85e3f061c4cc5a3603be2af 100644 (file)
@@ -23,7 +23,6 @@ import key from 'keymaster';
 import PropTypes from 'prop-types';
 import IssueView from './IssueView';
 import { updateIssue } from './actions';
-import { onFail } from '../../store/rootActions';
 import { setIssueAssignee } from '../../api/issues';
 /*:: import type { Issue as IssueType } from './types'; */
 import './Issue.css';
@@ -135,19 +134,11 @@ export default class Issue extends React.PureComponent {
   handleAssignement = (login /*: string */) => {
     const { issue } = this.props;
     if (issue.assignee !== login) {
-      updateIssue(
-        this.props.onChange,
-        this.handleFail,
-        setIssueAssignee({ issue: issue.key, assignee: login })
-      );
+      updateIssue(this.props.onChange, setIssueAssignee({ issue: issue.key, assignee: login }));
     }
     this.togglePopup('assign', false);
   };
 
-  handleFail = (error /*: Error */) => {
-    onFail(this.context.store.dispatch)(error);
-  };
-
   render() {
     return (
       <IssueView
@@ -161,7 +152,6 @@ export default class Issue extends React.PureComponent {
         onChange={this.props.onChange}
         onCheck={this.props.onCheck}
         onClick={this.props.onClick}
-        onFail={this.handleFail}
         onFilter={this.props.onFilter}
         selected={this.props.selected}
         togglePopup={this.togglePopup}
index 100e1942d232899847d5d1633e8aee72b69803e4..fbb2ac1e259793fa49d0bb43ce2ae0b859a70067 100644 (file)
@@ -39,7 +39,6 @@ type Props = {|
   onChange: Issue => void,
   onCheck?: (issue: string, event: Event) => void,
   onClick: string => void,
-  onFail: Error => void,
   onFilter?: (property: string, issue: Issue) => void,
   selected: boolean,
   togglePopup: (string, boolean | void) => void
@@ -64,11 +63,11 @@ export default class IssueView extends React.PureComponent {
   };
 
   editComment = (comment /*: string */, text /*: string */) => {
-    updateIssue(this.props.onChange, this.props.onFail, editIssueComment({ comment, text }));
+    updateIssue(this.props.onChange, editIssueComment({ comment, text }));
   };
 
   deleteComment = (comment /*: string */) => {
-    updateIssue(this.props.onChange, this.props.onFail, deleteIssueComment({ comment }));
+    updateIssue(this.props.onChange, deleteIssueComment({ comment }));
   };
 
   render() {
@@ -94,7 +93,6 @@ export default class IssueView extends React.PureComponent {
           displayLocationsCount={this.props.displayLocationsCount}
           displayLocationsLink={this.props.displayLocationsLink}
           issue={issue}
-          onFail={this.props.onFail}
           onFilter={this.props.onFilter}
           togglePopup={this.props.togglePopup}
         />
@@ -103,7 +101,6 @@ export default class IssueView extends React.PureComponent {
           issue={issue}
           onAssign={this.props.onAssign}
           onChange={this.props.onChange}
-          onFail={this.props.onFail}
           togglePopup={this.props.togglePopup}
         />
         {issue.comments &&
index 4d3c4c06bd9d593773ee21127df77aa4999e68c6..567b731c0bb67d946a7906fbfeb3dfde2944271e 100644 (file)
@@ -23,7 +23,6 @@ import { parseIssueFromResponse } from '../../helpers/issues';
 
 export const updateIssue = (
   onChange /*: Issue => void */,
-  onFail /*: Error => void */,
   resultPromise /*: Promise<*> */,
   oldIssue /*: ?Issue */,
   newIssue /*: ?Issue */
@@ -47,7 +46,6 @@ export const updateIssue = (
       }
     },
     error => {
-      onFail(error);
       if (optimisticUpdate) {
         // $FlowFixMe `oldIssue` is not null, because `optimisticUpdate` is true
         onChange(oldIssue);
index 0f12bf3a2267b7d2e90d4e0b7d166c6680f7e100..7167a3a87e2adcea3d1daae474785547fb2a7627 100644 (file)
@@ -36,7 +36,6 @@ type Props = {
   currentPopup: ?string,
   onAssign: string => void,
   onChange: Issue => void,
-  onFail: Error => void,
   togglePopup: (string, boolean | void) => void
 };
 */
@@ -64,7 +63,6 @@ export default class IssueActionsBar extends React.PureComponent {
       const newIssue = { ...issue, [property]: value };
       updateIssue(
         this.props.onChange,
-        this.props.onFail,
         apiCall({ issue: issue.key, [property]: value }),
         issue,
         newIssue
@@ -127,7 +125,6 @@ export default class IssueActionsBar extends React.PureComponent {
               isOpen={this.props.currentPopup === 'transition' && hasTransitions}
               issue={issue}
               onChange={this.handleTransition}
-              onFail={this.props.onFail}
               togglePopup={this.props.togglePopup}
             />
           </li>
@@ -138,7 +135,6 @@ export default class IssueActionsBar extends React.PureComponent {
                 isOpen={this.props.currentPopup === 'assign' && canAssign}
                 issue={issue}
                 onAssign={this.props.onAssign}
-                onFail={this.props.onFail}
                 togglePopup={this.props.togglePopup}
               />
             </li>
@@ -157,7 +153,6 @@ export default class IssueActionsBar extends React.PureComponent {
               currentPopup={this.props.currentPopup}
               issueKey={issue.key}
               onChange={this.props.onChange}
-              onFail={this.props.onFail}
               toggleComment={this.toggleComment}
             />
           )}
@@ -169,7 +164,6 @@ export default class IssueActionsBar extends React.PureComponent {
               isOpen={this.props.currentPopup === 'edit-tags' && canSetTags}
               issue={issue}
               onChange={this.props.onChange}
-              onFail={this.props.onFail}
               togglePopup={this.props.togglePopup}
             />
           </li>
index 0eb7f114d7029b0f3e96d827ad644e5d6851fb61..355b0effbdc69650e6ad020b5885133b6732851f 100644 (file)
@@ -33,7 +33,6 @@ type Props = {
   issue: Issue,
   canAssign: boolean,
   onAssign: string => void,
-  onFail: Error => void,
   togglePopup: (string, boolean | void) => void
 };
 */
@@ -78,13 +77,7 @@ export default class IssueAssign extends React.PureComponent {
             closeOnEscape={true}
             onRequestClose={this.handleClose}
             open={this.props.isOpen && this.props.canAssign}
-            overlay={
-              <SetAssigneePopup
-                issue={this.props.issue}
-                onFail={this.props.onFail}
-                onSelect={this.props.onAssign}
-              />
-            }>
+            overlay={<SetAssigneePopup issue={this.props.issue} onSelect={this.props.onAssign} />}>
             <Button
               className="button-link issue-action issue-action-with-options js-issue-assign"
               onClick={this.toggleAssign}>
index 0117454c1e9a21431cde836c84a04ecf8b7b59ed..d071ce91c7483b7972ddb32580b213755220b743 100644 (file)
@@ -33,8 +33,7 @@ type Props = {
   isOpen: boolean,
   issue: Issue,
   creationDate: string,
-  togglePopup: (string, boolean | void) => void,
-  onFail: Error => void
+  togglePopup: (string, boolean | void) => void
 };
 */
 
@@ -59,7 +58,7 @@ export default class IssueChangelog extends React.PureComponent {
         <Toggler
           onRequestClose={this.handleClose}
           open={this.props.isOpen}
-          overlay={<ChangelogPopup issue={this.props.issue} onFail={this.props.onFail} />}>
+          overlay={<ChangelogPopup issue={this.props.issue} />}>
           <Tooltip
             mouseEnterDelay={0.5}
             overlay={<DateTimeFormatter date={this.props.creationDate} />}>
index 5dfd3bfc69d8db3b4ba354c718a12683df85f1bf..f40031e0aafbb5a0f1074cfb7e61ad943fc006ae 100644 (file)
@@ -33,7 +33,6 @@ type Props = {|
   currentPopup: ?string,
   issueKey: string,
   onChange: Issue => void,
-  onFail: Error => void,
   toggleComment: (open?: boolean, placeholder?: string) => void
 |};
 */
@@ -42,11 +41,7 @@ export default class IssueCommentAction extends React.PureComponent {
   /*:: props: Props; */
 
   addComment = (text /*: string */) => {
-    updateIssue(
-      this.props.onChange,
-      this.props.onFail,
-      addIssueComment({ issue: this.props.issueKey, text })
-    );
+    updateIssue(this.props.onChange, addIssueComment({ issue: this.props.issueKey, text }));
     this.props.toggleComment(false);
   };
 
index 00912b7ca815430738bed2e4aa3abe57f0bfa7ba..868126e4d7db243b37e178afbbeb31d3c7beadc6 100644 (file)
@@ -34,7 +34,6 @@ type Props = {|
   isOpen: boolean,
   issue: Issue,
   onChange: Issue => void,
-  onFail: Error => void,
   togglePopup: (string, boolean | void) => void
 |};
 */
@@ -51,7 +50,6 @@ export default class IssueTags extends React.PureComponent {
     const newIssue = { ...issue, tags };
     updateIssue(
       this.props.onChange,
-      this.props.onFail,
       setIssueTags({ issue: issue.key, tags: tags.join(',') }),
       issue,
       newIssue
index 6c7ab58887a27bf402b406244b3afc432dbb0aa9..245240c46c5e88b22dbfbb21bae7b242c1db6e54 100644 (file)
@@ -40,7 +40,6 @@ type Props = {|
   displayLocationsCount?: boolean;
   displayLocationsLink?: boolean;
   issue: Issue,
-  onFail: Error => void,
   onFilter?: (property: string, issue: Issue) => void,
   togglePopup: (string, boolean | void) => void
 |};
@@ -89,7 +88,6 @@ export default function IssueTitleBar(props /*: Props */) {
               creationDate={issue.creationDate}
               isOpen={props.currentPopup === 'changelog'}
               issue={issue}
-              onFail={props.onFail}
               togglePopup={props.togglePopup}
             />
           </li>
@@ -125,7 +123,6 @@ export default function IssueTitleBar(props /*: Props */) {
               <SimilarIssuesFilter
                 isOpen={props.currentPopup === 'similarIssues'}
                 issue={issue}
-                onFail={props.onFail}
                 onFilter={props.onFilter}
                 togglePopup={props.togglePopup}
               />
index 52b17fba6044149a6ab9847a07a7c6838a837e64..f57008c66919fcbabc2afe26f95ac6080c37d183 100644 (file)
@@ -34,7 +34,6 @@ type Props = {
   isOpen: boolean,
   issue: Issue,
   onChange: Issue => void,
-  onFail: Error => void,
   togglePopup: (string, boolean | void) => void
 };
 */
@@ -45,7 +44,6 @@ export default class IssueTransition extends React.PureComponent {
   setTransition = (transition /*: string */) => {
     updateIssue(
       this.props.onChange,
-      this.props.onFail,
       setIssueTransition({ issue: this.props.issue.key, transition })
     );
     this.toggleSetTransition();
index 7126df242259b1071b7740044d2a48813667113c..a47d9fad24ec3368091862f1fed7688d469ecab4 100644 (file)
@@ -32,7 +32,6 @@ type Props = {|
   isOpen: boolean,
   issue: Issue,
   togglePopup: (string, boolean | void) => void,
-  onFail: Error => void,
   onFilter: (property: string, issue: Issue) => void
 |};
 */
index ad39643695eb2f206f5cc615dc11a85026486858..b426640ed3111f32e3f9be32d420fccaf3723f57 100644 (file)
@@ -35,7 +35,6 @@ it('should render without the action when the correct rights are missing', () =>
       isOpen={false}
       issue={issue}
       onAssign={jest.fn()}
-      onFail={jest.fn()}
       togglePopup={jest.fn()}
     />
   );
@@ -49,7 +48,6 @@ it('should render with the action', () => {
       isOpen={false}
       issue={issue}
       onAssign={jest.fn()}
-      onFail={jest.fn()}
       togglePopup={jest.fn()}
     />
   );
@@ -64,7 +62,6 @@ it('should open the popup when the button is clicked', () => {
       isOpen={false}
       issue={issue}
       onAssign={jest.fn()}
-      onFail={jest.fn()}
       togglePopup={toggle}
     />
   );
index 8e03fdef42aec2bab5b5cd8716ae1601cfcdb19e..fd81fcc373438139bb44844115c9eb08d46ab8e9 100644 (file)
@@ -34,7 +34,6 @@ it('should render correctly', () => {
       creationDate="2017-03-01T09:36:01+0100"
       isOpen={false}
       issue={issue}
-      onFail={jest.fn()}
       togglePopup={jest.fn()}
     />
   );
@@ -48,7 +47,6 @@ it('should open the popup when the button is clicked', () => {
       creationDate="2017-03-01T09:36:01+0100"
       isOpen={false}
       issue={issue}
-      onFail={jest.fn()}
       togglePopup={toggle}
     />
   );
index 2114f36622641a5d3f5ff9e2a0df1dce31619587..5a6317e63e0a0d82d1610804a56eeb7d243d8bbf 100644 (file)
@@ -27,7 +27,6 @@ it('should render correctly', () => {
     <IssueCommentAction
       currentPopup={null}
       issueKey="issue-key"
-      onFail={jest.fn()}
       onIssueChange={jest.fn()}
       toggleComment={jest.fn()}
     />
@@ -41,7 +40,6 @@ it('should open the popup when the button is clicked', () => {
     <IssueCommentAction
       currentPopup={null}
       issueKey="issue-key"
-      onFail={jest.fn()}
       onIssueChange={jest.fn()}
       toggleComment={toggle}
     />
index 81d9a512a45b076dee2d93b803f347281a42d14b..cf7efb121cd5130a4b27ce430ec178935f0a189f 100644 (file)
@@ -37,7 +37,6 @@ it('should render without the action when the correct rights are missing', () =>
         transitions: [],
         status: 'CLOSED'
       }}
-      onFail={jest.fn()}
       onIssueChange={jest.fn()}
       togglePopup={jest.fn()}
     />
@@ -51,7 +50,6 @@ it('should render with the action', () => {
       canSetTags={true}
       isOpen={false}
       issue={issue}
-      onFail={jest.fn()}
       onIssueChange={jest.fn()}
       togglePopup={jest.fn()}
     />
@@ -66,7 +64,6 @@ it('should open the popup when the button is clicked', () => {
       canSetTags={true}
       isOpen={false}
       issue={issue}
-      onFail={jest.fn()}
       onIssueChange={jest.fn()}
       togglePopup={toggle}
     />
index 4a917cf1ead44c27c67c50d317f1e9c79d32fffa..78ae2b7b1efdfc3b9c1fa3b8a771192ec0bc7c06 100644 (file)
@@ -52,7 +52,6 @@ it('should render the titlebar correctly', () => {
       branchLike={{ isMain: false, name: 'feature-1.0', type: 'SHORT' }}
       currentPopup={null}
       issue={issue}
-      onFail={jest.fn()}
       togglePopup={jest.fn()}
     />
   );
@@ -61,13 +60,7 @@ it('should render the titlebar correctly', () => {
 
 it('should render the titlebar with the filter', () => {
   const element = shallow(
-    <IssueTitleBar
-      currentPopup={null}
-      issue={issue}
-      onFail={jest.fn()}
-      onFilter={jest.fn()}
-      togglePopup={jest.fn()}
-    />
+    <IssueTitleBar currentPopup={null} issue={issue} onFilter={jest.fn()} togglePopup={jest.fn()} />
   );
   expect(element).toMatchSnapshot();
 });
index d768c0c852d51952dc66630d6d38651437d0a2b7..3ac74928b8fd369787f993fb04c572203e6b3779 100644 (file)
@@ -26,7 +26,6 @@ exports[`should open the popup when the button is clicked 2`] = `
             "assigneeName": "John Doe",
           }
         }
-        onFail={[MockFunction]}
         onSelect={[MockFunction]}
       />
     }
@@ -77,7 +76,6 @@ exports[`should render with the action 1`] = `
             "assigneeName": "John Doe",
           }
         }
-        onFail={[MockFunction]}
         onSelect={[MockFunction]}
       />
     }
index 7ba9bfa806c538af5682582a7e991c9ed7efa1ca..fad46a6b759839f10203b737465f1a0992bd3190 100644 (file)
@@ -25,7 +25,6 @@ exports[`should open the popup when the button is clicked 2`] = `
             "key": "issuekey",
           }
         }
-        onFail={[MockFunction]}
       />
     }
   >
@@ -73,7 +72,6 @@ exports[`should render correctly 1`] = `
             "key": "issuekey",
           }
         }
-        onFail={[MockFunction]}
       />
     }
   >
index b61379d35ab0e4017f6d49d7527e0b5e56c20cfd..9aed610d4ce15fb432e814449eed92b5dd0e268e 100644 (file)
@@ -48,7 +48,6 @@ exports[`should render the titlebar correctly 1`] = `
               },
             }
           }
-          onFail={[MockFunction]}
           togglePopup={[MockFunction]}
         />
       </li>
@@ -134,7 +133,6 @@ exports[`should render the titlebar with the filter 1`] = `
               },
             }
           }
-          onFail={[MockFunction]}
           togglePopup={[MockFunction]}
         />
       </li>
@@ -197,7 +195,6 @@ exports[`should render the titlebar with the filter 1`] = `
               },
             }
           }
-          onFail={[MockFunction]}
           onFilter={[MockFunction]}
           togglePopup={[MockFunction]}
         />
index a9ec681f27b861914f790fbb4354e00a697d7ee0..71c5d3b5b00169aa4e4962200725b5bb5c50be8d 100644 (file)
@@ -41,7 +41,6 @@ type Changelog = {
 /*::
 type Props = {
   issue: Issue,
-  onFail: Error => void,
   popupPosition?: {}
 };
 */
@@ -69,11 +68,14 @@ export default class ChangelogPopup extends React.PureComponent {
   }
 
   loadChangelog() {
-    getIssueChangelog(this.props.issue.key).then(changelogs => {
-      if (this.mounted) {
-        this.setState({ changelogs });
-      }
-    }, this.props.onFail);
+    getIssueChangelog(this.props.issue.key).then(
+      changelogs => {
+        if (this.mounted) {
+          this.setState({ changelogs });
+        }
+      },
+      () => {}
+    );
   }
 
   render() {
index a1758698ec8083b212722a9da22369e763f23ff1..434bfa9e19600a1fa0d6df3bff79e77240ef3bd5 100644 (file)
@@ -46,7 +46,6 @@ type User = {
 type Props = {
   currentUser: User,
   issue: Issue,
-  onFail: Error => void,
   onSelect: string => void,
   popupPosition?: {}
 };
@@ -91,11 +90,12 @@ class SetAssigneePopup extends React.PureComponent {
       organization: this.props.issue.projectOrganization,
       q: query,
       ps: LIST_SIZE
-    }).then(this.handleSearchResult, this.props.onFail);
+    }).then(this.handleSearchResult, () => {});
   };
 
-  searchUsers = (query /*: string */) =>
-    searchUsers({ q: query, ps: LIST_SIZE }).then(this.handleSearchResult, this.props.onFail);
+  searchUsers = (query /*: string */) => {
+    searchUsers({ q: query, ps: LIST_SIZE }).then(this.handleSearchResult, () => {});
+  };
 
   handleSearchResult = (data /*: Object */) => {
     this.setState({
index f08b7b05f6053d39867b258ec29506d1e5c64585..fa67372738e5209a4f31d9b377a3ec73e5d7bd9d 100644 (file)
@@ -29,7 +29,6 @@ it('should render the changelog popup correctly', () => {
         author: 'john.david.dalton@gmail.com',
         creationDate: '2017-03-01T09:36:01+0100'
       }}
-      onFail={jest.fn()}
     />
   );
   element.setState({
index 623292e882e5ffa1b08e25ec8b1763df59f4bdef..a5a8c4ae4732efec3145992c9a0241d87e180bfa 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 // @flow
-/*:: import type { Metric } from '../../store/metrics/actions'; */
+/*:: import type { Metric } from '../../app/flow-types'; */
 
 /*:: type MeasureIntern = {
   value?: string,
index 023a8ba563c56d2be2165775461fc9c7313cec07..2dcfdaeb7d66fd8a50846ba0581702e540018ad7 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { connect } from 'react-redux';
-import { getOrganizationByKey, areThereCustomOrganizations } from '../../store/rootReducer';
+import { getOrganizationByKey, areThereCustomOrganizations, Store } from '../../store/rootReducer';
 import OrganizationLink from '../ui/OrganizationLink';
 
 interface OwnProps {
@@ -30,7 +30,7 @@ interface Props {
   link?: boolean;
   linkClassName?: string;
   organization: { key: string; name: string } | null;
-  shouldBeDisplayed: boolean;
+  shouldBeDisplayed?: boolean;
 }
 
 function Organization(props: Props) {
@@ -54,11 +54,11 @@ function Organization(props: Props) {
   );
 }
 
-const mapStateToProps = (state: any, ownProps: OwnProps) => ({
+const mapStateToProps = (state: Store, ownProps: OwnProps) => ({
   organization: getOrganizationByKey(state, ownProps.organizationKey),
   shouldBeDisplayed: areThereCustomOrganizations(state)
 });
 
-export default connect<any, any, any>(mapStateToProps)(Organization);
+export default connect(mapStateToProps)(Organization);
 
 export const UnconnectedOrganization = Organization;
index 7657c13419c79e08f1bd7707f755786214aa624f..ad31cabb65d012c7756faa29084c20d4b0a84ef5 100644 (file)
@@ -21,7 +21,7 @@ import * as React from 'react';
 import { connect } from 'react-redux';
 import * as classNames from 'classnames';
 import GenericAvatar from './GenericAvatar';
-import { getGlobalSettingValue } from '../../store/rootReducer';
+import { getGlobalSettingValue, Store } from '../../store/rootReducer';
 
 interface Props {
   className?: string;
@@ -55,7 +55,7 @@ function Avatar(props: Props) {
   );
 }
 
-const mapStateToProps = (state: any) => ({
+const mapStateToProps = (state: Store) => ({
   enableGravatar: (getGlobalSettingValue(state, 'sonar.lf.enableGravatar') || {}).value === 'true',
   gravatarServerUrl: (getGlobalSettingValue(state, 'sonar.lf.gravatarServerUrl') || {}).value
 });
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 (file)
index 0000000..bc2ba11
--- /dev/null
@@ -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 (file)
index 0000000..4123354
--- /dev/null
@@ -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.ts b/server/sonar-web/src/main/js/store/appState.ts
new file mode 100644 (file)
index 0000000..19a5e54
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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 { ActionType } from './utils/actions';
+import { Extension, AppState } from '../app/types';
+import { EditionKey } from '../apps/marketplace/utils';
+
+type Action =
+  | ActionType<typeof setAppState, 'SET_APP_STATE'>
+  | ActionType<typeof setAdminPages, 'SET_ADMIN_PAGES'>
+  | ActionType<typeof requireAuthorization, 'REQUIRE_AUTHORIZATION'>;
+
+export function setAppState(appState: AppState) {
+  return { type: 'SET_APP_STATE', appState };
+}
+
+export function setAdminPages(adminPages: Extension[]) {
+  return { type: 'SET_ADMIN_PAGES', adminPages };
+}
+
+export function requireAuthorization() {
+  return { type: 'REQUIRE_AUTHORIZATION' };
+}
+
+const defaultValue: AppState = {
+  authenticationError: false,
+  authorizationError: false,
+  defaultOrganization: '',
+  edition: EditionKey.community,
+  organizationsEnabled: false,
+  productionDatabase: true,
+  qualifiers: [],
+  version: ''
+};
+
+export default function(state: AppState = defaultValue, action: Action): AppState {
+  if (action.type === 'SET_APP_STATE') {
+    return { ...state, ...action.appState };
+  }
+
+  if (action.type === 'SET_ADMIN_PAGES') {
+    return { ...state, adminPages: action.adminPages };
+  }
+
+  if (action.type === 'REQUIRE_AUTHORIZATION') {
+    return { ...state, authorizationError: true };
+  }
+
+  return state;
+}
diff --git a/server/sonar-web/src/main/js/store/appState/duck.ts b/server/sonar-web/src/main/js/store/appState/duck.ts
deleted file mode 100644 (file)
index 79ada58..0000000
+++ /dev/null
@@ -1,74 +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 { Extension, AppState } from '../../app/types';
-
-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;
-
-export function setAppState(appState: AppState): SetAppStateAction {
-  return {
-    type: 'SET_APP_STATE',
-    appState
-  };
-}
-
-export function setAdminPages(adminPages: Extension[]): SetAdminPagesAction {
-  return { type: 'SET_ADMIN_PAGES', adminPages };
-}
-
-export function requireAuthorization(): RequireAuthorizationAction {
-  return { type: 'REQUIRE_AUTHORIZATION' };
-}
-
-const defaultValue: AppState = {
-  authenticationError: false,
-  authorizationError: false,
-  organizationsEnabled: false,
-  qualifiers: []
-};
-
-export default function(state: AppState = defaultValue, action: Action): AppState {
-  if (action.type === 'SET_APP_STATE') {
-    return { ...state, ...action.appState };
-  }
-
-  if (action.type === 'SET_ADMIN_PAGES') {
-    return { ...state, adminPages: action.adminPages };
-  }
-
-  if (action.type === 'REQUIRE_AUTHORIZATION') {
-    return { ...state, authorizationError: true };
-  }
-
-  return state;
-}
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 (file)
index 0000000..370ecb1
--- /dev/null
@@ -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 (file)
index 81c4cba..0000000
+++ /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.ts b/server/sonar-web/src/main/js/store/languages.ts
new file mode 100644 (file)
index 0000000..2cc95fe
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 { keyBy } from 'lodash';
+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 };
+}
+
+export default function(state: Languages = {}, action: Action): Languages {
+  if (action.type === 'RECEIVE_LANGUAGES') {
+    return keyBy(action.languages, 'key');
+  }
+
+  return state;
+}
+
+export function getLanguages(state: Languages) {
+  return state;
+}
+
+export function getLanguageByKey(state: Languages, key: string) {
+  return state[key];
+}
diff --git a/server/sonar-web/src/main/js/store/languages/actions.js b/server/sonar-web/src/main/js/store/languages/actions.js
deleted file mode 100644 (file)
index 9b3d1a9..0000000
+++ /dev/null
@@ -1,25 +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.
- */
-export const RECEIVE_LANGUAGES = 'RECEIVE_LANGUAGES';
-
-export const receiveLanguages = languages => ({
-  type: RECEIVE_LANGUAGES,
-  languages
-});
diff --git a/server/sonar-web/src/main/js/store/languages/reducer.ts b/server/sonar-web/src/main/js/store/languages/reducer.ts
deleted file mode 100644 (file)
index 50caf69..0000000
+++ /dev/null
@@ -1,39 +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 { keyBy } from 'lodash';
-import { RECEIVE_LANGUAGES } from './actions';
-
-export interface Languages {
-  [key: string]: { key: string; name: string };
-}
-
-const reducer = (state: Languages = {}, action: any = {}) => {
-  if (action.type === RECEIVE_LANGUAGES) {
-    return keyBy(action.languages, 'key');
-  }
-
-  return state;
-};
-
-export default reducer;
-
-export const getLanguages = (state: Languages) => state;
-
-export const getLanguageByKey = (state: Languages, key: string) => state[key];
diff --git a/server/sonar-web/src/main/js/store/metrics.ts b/server/sonar-web/src/main/js/store/metrics.ts
new file mode 100644 (file)
index 0000000..8509890
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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 { keyBy } from 'lodash';
+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 Action = ActionType<typeof receiveMetrics, '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: State['keys'] = [], action: Action) => {
+  if (action.type === 'RECEIVE_METRICS') {
+    return action.metrics.map(f => f.key);
+  }
+
+  return state;
+};
+
+export default combineReducers({ byKey, 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 (file)
index a271928..0000000
+++ /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/metrics/reducer.js b/server/sonar-web/src/main/js/store/metrics/reducer.js
deleted file mode 100644 (file)
index d2d5284..0000000
+++ /dev/null
@@ -1,50 +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 { combineReducers } from 'redux';
-import { keyBy } from 'lodash';
-import { RECEIVE_METRICS } from './actions';
-/*:: import type { Metric } from './actions'; */
-
-/*::
-type StateByKey = { [string]: Metric };
-type StateKeys = Array<string>;
-type State = { byKey: StateByKey, keys: StateKeys };
-*/
-
-const byKey = (state /*: StateByKey */ = {}, action = {}) => {
-  if (action.type === RECEIVE_METRICS) {
-    return keyBy(action.metrics, 'key');
-  }
-  return state;
-};
-
-const keys = (state /*: StateKeys */ = [], action = {}) => {
-  if (action.type === RECEIVE_METRICS) {
-    return action.metrics.map(f => f.key);
-  }
-
-  return state;
-};
-
-export default combineReducers({ byKey, keys });
-
-export const getMetrics = (state /*: State */) => state.byKey;
-export const getMetricsKey = (state /*: State */) => state.keys;
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 (file)
index 0000000..6b1dfdc
--- /dev/null
@@ -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 (file)
index c5ad9b7..0000000
+++ /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 (file)
index e9321e2..0000000
+++ /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 (file)
index 5f01ac9..0000000
+++ /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 (file)
index c49e392..0000000
+++ /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 (file)
index 0000000..52ab8e7
--- /dev/null
@@ -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 (file)
index 16d038d..0000000
+++ /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 (file)
index 0000000..8dfc49d
--- /dev/null
@@ -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 (file)
index 0000000..969cf8a
--- /dev/null
@@ -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 (file)
index 9c9a146..0000000
+++ /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 (file)
index a6e79db..0000000
+++ /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/utils/actions.ts b/server/sonar-web/src/main/js/store/utils/actions.ts
new file mode 100644 (file)
index 0000000..4926a67
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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 { Omit } from '../../app/types';
+
+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.js
deleted file mode 100644 (file)
index 75bb516..0000000
+++ /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.
- */
-import { createStore, applyMiddleware, compose } from 'redux';
-import thunk from 'redux-thunk';
-
-const middlewares = [thunk];
-const composed = [];
-
-if (process.env.NODE_ENV === 'development') {
-  const { createLogger } = require('redux-logger');
-  middlewares.push(createLogger());
-
-  composed.push(window.devToolsExtension ? window.devToolsExtension() : f => f);
-}
-
-const finalCreateStore = compose(
-  applyMiddleware(...middlewares),
-  ...composed
-)(createStore);
-
-export default function configureStore(rootReducer, initialState) {
-  return finalCreateStore(rootReducer, initialState);
-}
diff --git a/server/sonar-web/src/main/js/store/utils/configureStore.ts b/server/sonar-web/src/main/js/store/utils/configureStore.ts
new file mode 100644 (file)
index 0000000..8681815
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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 { 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 = [];
+
+if (process.env.NODE_ENV === 'development') {
+  const { createLogger } = require('redux-logger');
+  middlewares.push(createLogger());
+
+  const { devToolsExtension } = window as any;
+  composed.push(devToolsExtension ? devToolsExtension() : (f: Function) => f);
+}
+
+const finalCreateStore = compose(
+  applyMiddleware(...middlewares),
+  ...composed
+)(createStore);
+
+export default function configureStore(rootReducer: RootReducer, initialState?: State) {
+  return finalCreateStore(rootReducer, initialState);
+}