aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2017-12-11 17:58:18 +0100
committerStas Vilchik <stas.vilchik@sonarsource.com>2018-01-02 10:38:10 +0100
commitd04588cdf547d0e8f9290ad1cf1fa3fb0ecf6371 (patch)
treee3621c0f0cc43fa2269027a3cb0ec56ec4c68320 /server/sonar-web
parent7260d343ea6a7289695a8c509860cbcf726e433c (diff)
downloadsonarqube-d04588cdf547d0e8f9290ad1cf1fa3fb0ecf6371.tar.gz
sonarqube-d04588cdf547d0e8f9290ad1cf1fa3fb0ecf6371.zip
SONAR-10186 fetch user organizations when app starts
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/app/components/App.tsx35
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.tsx16
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUserContainer.tsx11
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavUser-test.tsx79
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.tsx.snap77
-rw-r--r--server/sonar-web/src/main/js/app/types.ts1
-rw-r--r--server/sonar-web/src/main/js/apps/account/organizations/UserOrganizations.tsx9
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<any>;
fetchCurrentUser: () => Promise<void>;
fetchLanguages: () => Promise<void>;
+ fetchMyOrganizations: () => Promise<void>;
}
interface State {
@@ -66,7 +68,17 @@ class App extends React.PureComponent<Props, State> {
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<Props, State> {
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 <GlobalLoading />;
@@ -102,4 +108,9 @@ class App extends React.PureComponent<Props, State> {
}
}
-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<void>;
organizations: Organization[];
}
@@ -91,10 +90,8 @@ export default class GlobalNavUser extends React.PureComponent<Props, State> {
};
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<Props, State> {
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<Props, State> {
<OrganizationAvatar organization={organization} small={true} />
<span className="spacer-left">{organization.name}</span>
</div>
- {organization.canAdmin && (
+ {organization.isAdmin && (
<span className="outline-badge spacer-left">{translate('admin')}</span>
)}
</OrganizationLink>
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<void>;
-}
-
-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(
- <GlobalNavUser
- appState={appState}
- currentUser={currentUser}
- fetchMyOrganizations={jest.fn()}
- organizations={[]}
- />
+ <GlobalNavUser appState={appState} currentUser={currentUser} organizations={[]} />
);
expect(wrapper).toMatchSnapshot();
});
it('should render the right interface for logged in user', () => {
const wrapper = shallow(
- <GlobalNavUser
- appState={appState}
- currentUser={currentUser}
- fetchMyOrganizations={jest.fn()}
- organizations={[]}
- />
+ <GlobalNavUser appState={appState} currentUser={currentUser} organizations={[]} />
);
wrapper.setState({ open: true });
expect(wrapper).toMatchSnapshot();
});
-it('should render the users organizations', () => {
+it('should render user organizations', () => {
const wrapper = shallow(
- <GlobalNavUser
- appState={appState}
- currentUser={currentUser}
- fetchMyOrganizations={jest.fn()}
- organizations={organizations}
- />
+ <GlobalNavUser appState={appState} currentUser={currentUser} organizations={organizations} />
);
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(
<GlobalNavUser
appState={{ organizationsEnabled: false }}
currentUser={currentUser}
- fetchMyOrganizations={jest.fn()}
organizations={organizations}
/>
);
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(
- <GlobalNavUser
- appState={appState}
- currentUser={currentUser}
- fetchMyOrganizations={fetchMyOrganizations}
- organizations={[]}
- />
- );
- 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(
- <GlobalNavUser
- appState={appState}
- currentUser={currentUser}
- fetchMyOrganizations={fetchMyOrganizations}
- organizations={organizations}
- />
- );
- 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(
- <GlobalNavUser
- appState={appState}
- currentUser={currentUser}
- fetchMyOrganizations={fetchMyOrganizations}
- organizations={organizations}
- />
- );
- (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`] = `
<li
className="dropdown js-user-authenticated open"
>
@@ -134,7 +134,7 @@ exports[`should render the right interface for logged in user 1`] = `
</li>
`;
-exports[`should render the users organizations 1`] = `
+exports[`should render user organizations 1`] = `
<li
className="dropdown js-user-authenticated open"
>
@@ -311,76 +311,3 @@ exports[`should render the users organizations 1`] = `
</ul>
</li>
`;
-
-exports[`should update the component correctly when the user changes to anonymous 1`] = `
-<li
- className="dropdown js-user-authenticated open"
->
- <a
- className="dropdown-toggle navbar-avatar"
- href="#"
- onClick={[Function]}
- >
- <Connect(Avatar)
- hash="abcd1234"
- name="foo"
- size={32}
- />
- </a>
- <ul
- className="dropdown-menu dropdown-menu-right"
- >
- <li
- className="dropdown-item"
- >
- <div
- className="text-ellipsis text-muted"
- title="foo"
- >
- <strong>
- foo
- </strong>
- </div>
- <div
- className="little-spacer-top text-ellipsis text-muted"
- title="foo@bar.baz"
- >
- foo@bar.baz
- </div>
- </li>
- <li
- className="divider"
- />
- <li>
- <Link
- onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to="/account"
- >
- my_account.page
- </Link>
- </li>
- <li>
- <a
- href="#"
- onClick={[Function]}
- >
- layout.logout
- </a>
- </li>
- </ul>
-</li>
-`;
-
-exports[`should update the component correctly when the user changes to anonymous 2`] = `
-<li>
- <a
- className="navbar-login"
- href="#"
- onClick={[Function]}
- >
- layout.login
- </a>
-</li>
-`;
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<void>;
- fetchMyOrganizations: () => Promise<void>;
}
interface Props extends StateProps, DispatchProps {}
@@ -51,10 +50,7 @@ class UserOrganizations extends React.PureComponent<Props, State> {
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;