diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-11-28 15:56:44 +0100 |
---|---|---|
committer | Teryk Bellahsene <teryk@users.noreply.github.com> | 2017-11-29 17:27:53 +0100 |
commit | d1157c4101390a94167aebad27d054c93e0d66f4 (patch) | |
tree | 91c6093d3ee03bdc09f23291b5d00f8707a0c5cb /server/sonar-web/src | |
parent | 010c43a56291ed3e97ad0a0d687d6f709bb460d7 (diff) | |
download | sonarqube-d1157c4101390a94167aebad27d054c93e0d66f4.tar.gz sonarqube-d1157c4101390a94167aebad27d054c93e0d66f4.zip |
SONAR-9000 display all organizations a user is a member of
Diffstat (limited to 'server/sonar-web/src')
12 files changed, 77 insertions, 52 deletions
diff --git a/server/sonar-web/src/main/js/api/organizations.ts b/server/sonar-web/src/main/js/api/organizations.ts index 408f6b65cb2..f2ffd3f6fe7 100644 --- a/server/sonar-web/src/main/js/api/organizations.ts +++ b/server/sonar-web/src/main/js/api/organizations.ts @@ -20,11 +20,17 @@ import { getJSON, post, postJSON, RequestData } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; +interface GetOrganizationsParameters { + organizations?: string; + member?: boolean; +} + interface GetOrganizationsResponse { organizations: Array<{ avatar?: string; description?: string; guarded: boolean; + isAdmin: boolean; key: string; name: string; url?: string; @@ -36,20 +42,14 @@ interface GetOrganizationsResponse { }; } -export function getOrganizations(organizations?: string[]): Promise<GetOrganizationsResponse> { - const data: RequestData = {}; - if (organizations) { - Object.assign(data, { organizations: organizations.join() }); - } +export function getOrganizations( + data: GetOrganizationsParameters +): Promise<GetOrganizationsResponse> { return getJSON('/api/organizations/search', data); } -export function getMyOrganizations(): Promise<any> { - return getJSON('/api/organizations/search_my_organizations').then(r => r.organizations); -} - export function getOrganization(key: string): Promise<any> { - return getOrganizations([key]) + return getOrganizations({ organizations: key }) .then(r => r.organizations.find((o: any) => o.key === key)) .catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js index 761cff9cde4..cb70ab05709 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js @@ -44,7 +44,7 @@ type Props = { currentUser: CurrentUser, fetchMyOrganizations: () => Promise<*>, location: Object, - organizations: Array<{ key: string, name: string }>, + organizations: Array<{ isAdmin: bool, key: string, name: string }>, router: { push: string => void } }; */ @@ -157,9 +157,17 @@ export default class GlobalNavUser extends React.PureComponent { {hasOrganizations && sortBy(organizations, org => org.name.toLowerCase()).map(organization => ( <li key={organization.key}> - <OrganizationLink organization={organization} onClick={this.closeDropdown}> - <OrganizationIcon /> - <span className="spacer-left">{organization.name}</span> + <OrganizationLink + className="dropdown-item-flex" + organization={organization} + onClick={this.closeDropdown}> + <div> + <OrganizationIcon /> + <span className="spacer-left">{organization.name}</span> + </div> + {organization.isAdmin && ( + <span className="outline-badge spacer-left">{translate('admin')}</span> + )} </OrganizationLink> </li> ))} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap index d603a20a60f..fb7c3c4e59e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap @@ -201,6 +201,7 @@ exports[`should render the users organizations 1`] = ` key="bar" > <OrganizationLink + className="dropdown-item-flex" onClick={[Function]} organization={ Object { @@ -209,18 +210,21 @@ exports[`should render the users organizations 1`] = ` } } > - <OrganizationIcon /> - <span - className="spacer-left" - > - bar - </span> + <div> + <OrganizationIcon /> + <span + className="spacer-left" + > + bar + </span> + </div> </OrganizationLink> </li> <li key="foo" > <OrganizationLink + className="dropdown-item-flex" onClick={[Function]} organization={ Object { @@ -229,18 +233,21 @@ exports[`should render the users organizations 1`] = ` } } > - <OrganizationIcon /> - <span - className="spacer-left" - > - Foo - </span> + <div> + <OrganizationIcon /> + <span + className="spacer-left" + > + Foo + </span> + </div> </OrganizationLink> </li> <li key="myorg" > <OrganizationLink + className="dropdown-item-flex" onClick={[Function]} organization={ Object { @@ -249,12 +256,14 @@ exports[`should render the users organizations 1`] = ` } } > - <OrganizationIcon /> - <span - className="spacer-left" - > - MyOrg - </span> + <div> + <OrganizationIcon /> + <span + className="spacer-left" + > + MyOrg + </span> + </div> </OrganizationLink> </li> <li diff --git a/server/sonar-web/src/main/js/app/styles/components/dropdowns.css b/server/sonar-web/src/main/js/app/styles/components/dropdowns.css index 6de1a659782..3d7e09d3457 100644 --- a/server/sonar-web/src/main/js/app/styles/components/dropdowns.css +++ b/server/sonar-web/src/main/js/app/styles/components/dropdowns.css @@ -176,3 +176,9 @@ color: var(--secondFontColor); font-size: 11px; } + +.dropdown-item-flex { + display: flex !important; + justify-content: space-between; + align-items: center; +} diff --git a/server/sonar-web/src/main/js/apps/account/organizations/OrganizationCard.js b/server/sonar-web/src/main/js/apps/account/organizations/OrganizationCard.js index a76d12612b1..1bded7bb20f 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/OrganizationCard.js +++ b/server/sonar-web/src/main/js/apps/account/organizations/OrganizationCard.js @@ -51,6 +51,9 @@ export default function OrganizationCard(props /*: Props */) { <h3 className="account-project-name"> <OrganizationLink organization={organization}>{organization.name}</OrganizationLink> + {organization.isAdmin && ( + <span className="outline-badge spacer-left">{translate('admin')}</span> + )} </h3> {!!organization.description && ( diff --git a/server/sonar-web/src/main/js/apps/account/organizations/actions.js b/server/sonar-web/src/main/js/apps/account/organizations/actions.js index da353d90380..38f30b159b0 100644 --- a/server/sonar-web/src/main/js/apps/account/organizations/actions.js +++ b/server/sonar-web/src/main/js/apps/account/organizations/actions.js @@ -17,20 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import * as api from '../../../api/organizations'; +import { getOrganizations } from '../../../api/organizations'; import { receiveMyOrganizations } from '../../../store/organizations/duck'; import { getValues } from '../../../api/settings'; import { receiveValues } from '../../settings/store/values/actions'; export const fetchMyOrganizations = () => dispatch => { - return api.getMyOrganizations().then(keys => { - if (keys.length > 0) { - return api.getOrganizations(keys).then(({ organizations }) => { - return dispatch(receiveMyOrganizations(organizations)); - }); - } else { - return dispatch(receiveMyOrganizations([])); - } + return getOrganizations({ member: true }).then(({ organizations }) => { + return dispatch(receiveMyOrganizations(organizations)); }); }; diff --git a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js index c7c6608573f..d3af96718f6 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js +++ b/server/sonar-web/src/main/js/apps/issues/components/AppContainer.js @@ -41,7 +41,7 @@ const fetchIssueOrganizations = issues => dispatch => { } const organizationKeys = uniq(issues.map(issue => issue.organization)); - return getOrganizations(organizationKeys).then( + return getOrganizations({ organizations: organizationKeys.join() }).then( response => dispatch(receiveOrganizations(response.organizations)), throwGlobalError ); diff --git a/server/sonar-web/src/main/js/apps/projects/utils.ts b/server/sonar-web/src/main/js/apps/projects/utils.ts index 7ad9dd4e7c0..daa94d94a4d 100644 --- a/server/sonar-web/src/main/js/apps/projects/utils.ts +++ b/server/sonar-web/src/main/js/apps/projects/utils.ts @@ -270,7 +270,7 @@ function fetchProjectOrganizations(projects: Array<{ organization: string }>) { } const organizations = uniq(projects.map(project => project.organization)); - return getOrganizations(organizations).then(r => r.organizations); + return getOrganizations({ organizations: organizations.join() }).then(r => r.organizations); } function mapFacetValues(values: Array<{ val: string; count: number }>) { diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js index e5b5c49ed82..4db0fec476c 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js @@ -23,7 +23,7 @@ import classNames from 'classnames'; import { sortBy } from 'lodash'; import Step from './Step'; import NewOrganizationForm from './NewOrganizationForm'; -import { getMyOrganizations } from '../../../api/organizations'; +import { getOrganizations } from '../../../api/organizations'; import Select from '../../../components/controls/Select'; import { translate } from '../../../helpers/l10n'; @@ -68,13 +68,15 @@ export default class OrganizationStep extends React.PureComponent { } fetchOrganizations = () => { - getMyOrganizations().then( - organizations => { + getOrganizations({ member: true }).then( + ({ organizations }) => { if (this.mounted) { + const organizationKeys = organizations.map(o => o.key); // best guess: if there is only one organization, then it is personal // otherwise, we can't guess, let's display them all as just "existing organizations" - const personalOrganization = organizations.length === 1 ? organizations[0] : undefined; - const existingOrganizations = organizations.length > 1 ? sortBy(organizations) : []; + const personalOrganization = + organizationKeys.length === 1 ? organizationKeys[0] : undefined; + const existingOrganizations = organizationKeys.length > 1 ? sortBy(organizationKeys) : []; const selection = personalOrganization ? 'personal' : existingOrganizations.length > 0 ? 'existing' : 'new'; diff --git a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js index 6bc5530cd54..be9c2542a87 100644 --- a/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js +++ b/server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js @@ -22,16 +22,18 @@ import React from 'react'; import { mount } from 'enzyme'; import OrganizationStep from '../OrganizationStep'; import { click } from '../../../../helpers/testUtils'; -import { getMyOrganizations } from '../../../../api/organizations'; +import { getOrganizations } from '../../../../api/organizations'; jest.mock('../../../../api/organizations', () => ({ - getMyOrganizations: jest.fn(() => Promise.resolve(['user', 'another'])) + getOrganizations: jest.fn(() => + Promise.resolve({ organizations: [{ key: 'user' }, { key: 'another' }] }) + ) })); const currentUser = { isLoggedIn: true, login: 'user' }; beforeEach(() => { - getMyOrganizations.mockClear(); + getOrganizations.mockClear(); }); // FIXME diff --git a/server/sonar-web/src/main/js/store/organizations/duck.js b/server/sonar-web/src/main/js/store/organizations/duck.js index 77ebd43e2f3..4b4023f97ce 100644 --- a/server/sonar-web/src/main/js/store/organizations/duck.js +++ b/server/sonar-web/src/main/js/store/organizations/duck.js @@ -30,6 +30,7 @@ export type Organization = { canProvisionProjects?: boolean, canUpdateProjectsVisibilityToPrivate?: boolean, description?: string, + isAdmin: bool, key: string, name: string, pages?: Array<{ key: string, name: string }>, diff --git a/server/sonar-web/src/main/js/store/rootActions.js b/server/sonar-web/src/main/js/store/rootActions.js index 3775e1d0391..00836bfe58e 100644 --- a/server/sonar-web/src/main/js/store/rootActions.js +++ b/server/sonar-web/src/main/js/store/rootActions.js @@ -45,7 +45,7 @@ export const fetchMetrics = () => dispatch => getAllMetrics().then(metrics => dispatch(receiveMetrics(metrics)), onFail(dispatch)); export const fetchOrganizations = (organizations /*: Array<string> | void */) => dispatch => - getOrganizations(organizations).then( + getOrganizations({ organizations: organizations && organizations.join() }).then( r => dispatch(receiveOrganizations(r.organizations)), onFail(dispatch) ); |