From d04588cdf547d0e8f9290ad1cf1fa3fb0ecf6371 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Mon, 11 Dec 2017 17:58:18 +0100 Subject: [PATCH] SONAR-10186 fetch user organizations when app starts --- .../src/main/js/app/components/App.tsx | 35 +++++--- .../components/nav/global/GlobalNavUser.tsx | 16 +--- .../nav/global/GlobalNavUserContainer.tsx | 11 +-- .../global/__tests__/GlobalNavUser-test.tsx | 79 ++----------------- .../__snapshots__/GlobalNavUser-test.tsx.snap | 77 +----------------- server/sonar-web/src/main/js/app/types.ts | 1 + .../organizations/UserOrganizations.tsx | 9 +-- 7 files changed, 37 insertions(+), 191 deletions(-) diff --git a/server/sonar-web/src/main/js/app/components/App.tsx b/server/sonar-web/src/main/js/app/components/App.tsx index 44528fc0b4d..9dd53d13369 100644 --- a/server/sonar-web/src/main/js/app/components/App.tsx +++ b/server/sonar-web/src/main/js/app/components/App.tsx @@ -23,12 +23,14 @@ import * as PropTypes from 'prop-types'; import GlobalLoading from './GlobalLoading'; import { fetchCurrentUser } from '../../store/users/actions'; import { fetchLanguages, fetchAppState } from '../../store/rootActions'; +import { fetchMyOrganizations } from '../../apps/account/organizations/actions'; interface Props { children: JSX.Element; fetchAppState: () => Promise; fetchCurrentUser: () => Promise; fetchLanguages: () => Promise; + fetchMyOrganizations: () => Promise; } interface State { @@ -66,7 +68,17 @@ class App extends React.PureComponent { this.props .fetchCurrentUser() .then(() => Promise.all([this.fetchAppState(), this.props.fetchLanguages()])) - .then(this.finishLoading, () => {}); + .then( + ([appState]) => { + if (this.mounted) { + if (appState.organizationsEnabled) { + this.props.fetchMyOrganizations(); + } + this.setState({ loading: false }); + } + }, + () => {} + ); } componentWillUnmount() { @@ -76,24 +88,18 @@ class App extends React.PureComponent { fetchAppState = () => { return this.props.fetchAppState().then(appState => { if (this.mounted) { - const onSonarCloud = - appState.settings !== undefined && - appState.settings['sonar.sonarcloud.enabled'] === 'true'; this.setState({ branchesEnabled: appState.branchesEnabled, canAdmin: appState.canAdmin, - onSonarCloud + onSonarCloud: + appState.settings !== undefined && + appState.settings['sonar.sonarcloud.enabled'] === 'true' }); } + return appState; }); }; - finishLoading = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - }; - render() { if (this.state.loading) { return ; @@ -102,4 +108,9 @@ class App extends React.PureComponent { } } -export default connect(null, { fetchAppState, fetchCurrentUser, fetchLanguages })(App as any); +export default connect(null, { + fetchAppState, + fetchCurrentUser, + fetchLanguages, + fetchMyOrganizations +})(App as any); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.tsx index 3a5a143f963..1649a5e370e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.tsx @@ -33,7 +33,6 @@ import OrganizationAvatar from '../../../../components/common/OrganizationAvatar interface Props { appState: { organizationsEnabled: boolean }; currentUser: CurrentUser; - fetchMyOrganizations: () => Promise; organizations: Organization[]; } @@ -91,10 +90,8 @@ export default class GlobalNavUser extends React.PureComponent { }; openDropdown = () => { - this.fetchMyOrganizations().then(() => { - window.addEventListener('click', this.handleClickOutside, true); - this.setState({ open: true }); - }); + window.addEventListener('click', this.handleClickOutside, true); + this.setState({ open: true }); }; closeDropdown = () => { @@ -102,13 +99,6 @@ export default class GlobalNavUser extends React.PureComponent { this.setState({ open: false }); }; - fetchMyOrganizations = () => { - if (this.props.appState.organizationsEnabled) { - return this.props.fetchMyOrganizations(); - } - return Promise.resolve(); - }; - renderAuthenticated() { const { organizations } = this.props; const currentUser = this.props.currentUser as LoggedInUser; @@ -163,7 +153,7 @@ export default class GlobalNavUser extends React.PureComponent { {organization.name} - {organization.canAdmin && ( + {organization.isAdmin && ( {translate('admin')} )} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.tsx index f73200791a0..eaff69f9a86 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.tsx @@ -20,7 +20,6 @@ import { connect } from 'react-redux'; import GlobalNavUser from './GlobalNavUser'; import { Organization } from '../../../types'; -import { fetchMyOrganizations } from '../../../../apps/account/organizations/actions'; import { getMyOrganizations } from '../../../../store/rootReducer'; interface StateProps { @@ -31,12 +30,4 @@ const mapStateToProps = (state: any): StateProps => ({ organizations: getMyOrganizations(state) }); -interface DispatchProps { - fetchMyOrganizations: () => Promise; -} - -const mapDispatchToProps = { - fetchMyOrganizations: fetchMyOrganizations as any -} as DispatchProps; - -export default connect(mapStateToProps, mapDispatchToProps)(GlobalNavUser); +export default connect(mapStateToProps)(GlobalNavUser); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.tsx index 90cf0393293..659fe6def16 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.tsx @@ -33,104 +33,35 @@ const appState = { organizationsEnabled: true }; it('should render the right interface for anonymous user', () => { const currentUser = { isLoggedIn: false }; const wrapper = shallow( - + ); expect(wrapper).toMatchSnapshot(); }); it('should render the right interface for logged in user', () => { const wrapper = shallow( - + ); wrapper.setState({ open: true }); expect(wrapper).toMatchSnapshot(); }); -it('should render the users organizations', () => { +it('should render user organizations', () => { const wrapper = shallow( - + ); wrapper.setState({ open: true }); expect(wrapper).toMatchSnapshot(); }); -it('should not render the users organizations when they are not activated', () => { +it('should not render user organizations when they are not activated', () => { const wrapper = shallow( ); wrapper.setState({ open: true }); expect(wrapper).toMatchSnapshot(); }); - -it('should update the component correctly when the user changes to anonymous', () => { - const fetchMyOrganizations = jest.fn(); - const wrapper = shallow( - - ); - wrapper.setState({ open: true }); - expect(wrapper).toMatchSnapshot(); - wrapper.setProps({ currentUser: { isLoggedIn: false } }); - expect(fetchMyOrganizations.mock.calls.length).toBe(0); - expect(wrapper).toMatchSnapshot(); -}); - -it('should lazyload the organizations when opening the dropdown', () => { - const fetchMyOrganizations = jest.fn(() => Promise.resolve()); - const wrapper = shallow( - - ); - expect(fetchMyOrganizations.mock.calls.length).toBe(0); - (wrapper.instance() as GlobalNavUser).openDropdown(); - expect(fetchMyOrganizations.mock.calls.length).toBe(1); - (wrapper.instance() as GlobalNavUser).openDropdown(); - expect(fetchMyOrganizations.mock.calls.length).toBe(2); -}); - -it('should update the organizations when the user changes', () => { - const fetchMyOrganizations = jest.fn(() => Promise.resolve()); - const wrapper = shallow( - - ); - (wrapper.instance() as GlobalNavUser).openDropdown(); - expect(fetchMyOrganizations.mock.calls.length).toBe(1); - wrapper.setProps({ - currentUser: { isLoggedIn: true, name: 'test', email: 'test@sonarsource.com' } - }); - (wrapper.instance() as GlobalNavUser).openDropdown(); - expect(fetchMyOrganizations.mock.calls.length).toBe(2); -}); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap index 3bb56aac675..23fef6f2d99 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should not render the users organizations when they are not activated 1`] = ` +exports[`should not render user organizations when they are not activated 1`] = `
  • @@ -134,7 +134,7 @@ exports[`should render the right interface for logged in user 1`] = `
  • `; -exports[`should render the users organizations 1`] = ` +exports[`should render user organizations 1`] = `
  • @@ -311,76 +311,3 @@ exports[`should render the users organizations 1`] = `
  • `; - -exports[`should update the component correctly when the user changes to anonymous 1`] = ` -
  • - - - - -
  • -`; - -exports[`should update the component correctly when the user changes to anonymous 2`] = ` -
  • - - layout.login - -
  • -`; diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index a180856c47a..af3c21a26db 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -115,6 +115,7 @@ export interface Organization { canProvisionProjects?: boolean; canUpdateProjectsVisibilityToPrivate?: boolean; description?: string; + isAdmin?: boolean; isDefault?: boolean; key: string; name: string; diff --git a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx index c33f2be07eb..e6f34fbd8e2 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx +++ b/server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx @@ -23,7 +23,7 @@ import { connect } from 'react-redux'; import OrganizationsList from './OrganizationsList'; import CreateOrganizationForm from './CreateOrganizationForm'; import { translate } from '../../../helpers/l10n'; -import { fetchIfAnyoneCanCreateOrganizations, fetchMyOrganizations } from './actions'; +import { fetchIfAnyoneCanCreateOrganizations } from './actions'; import { getAppState, getMyOrganizations, getGlobalSettingValue } from '../../../store/rootReducer'; import { Organization } from '../../../app/types'; @@ -35,7 +35,6 @@ interface StateProps { interface DispatchProps { fetchIfAnyoneCanCreateOrganizations: () => Promise; - fetchMyOrganizations: () => Promise; } interface Props extends StateProps, DispatchProps {} @@ -51,10 +50,7 @@ class UserOrganizations extends React.PureComponent { componentDidMount() { this.mounted = true; - Promise.all([ - this.props.fetchMyOrganizations(), - this.props.fetchIfAnyoneCanCreateOrganizations() - ]).then(this.stopLoading, this.stopLoading); + this.props.fetchIfAnyoneCanCreateOrganizations().then(this.stopLoading, this.stopLoading); } componentWillUnmount() { @@ -133,7 +129,6 @@ const mapStateToProps = (state: any): StateProps => ({ }); const mapDispatchToProps = { - fetchMyOrganizations: fetchMyOrganizations as any, fetchIfAnyoneCanCreateOrganizations: fetchIfAnyoneCanCreateOrganizations as any } as DispatchProps; -- 2.39.5