diff options
author | Mathieu Suen <mathieu.suen@sonarsource.com> | 2020-10-07 10:24:33 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-10-16 20:07:40 +0000 |
commit | 16df95a4fa061755d0129927e29a3e7dc4794e8d (patch) | |
tree | 6c631c5bc520a2f9222ba974184bd053508d1bfe /server/sonar-web/src/main | |
parent | 4d8f0c4bf3214a0a36e15ada2a8b1090543eaf8e (diff) | |
download | sonarqube-16df95a4fa061755d0129927e29a3e7dc4794e8d.tar.gz sonarqube-16df95a4fa061755d0129927e29a3e7dc4794e8d.zip |
SONAR-13936 Remove org on user and group API front-end.
Diffstat (limited to 'server/sonar-web/src/main')
43 files changed, 433 insertions, 365 deletions
diff --git a/server/sonar-web/src/main/js/api/permissions.ts b/server/sonar-web/src/main/js/api/permissions.ts index 53222bd2212..d87932fe7cd 100644 --- a/server/sonar-web/src/main/js/api/permissions.ts +++ b/server/sonar-web/src/main/js/api/permissions.ts @@ -27,7 +27,6 @@ export function grantPermissionToUser(data: { projectKey?: string; login: string; permission: string; - organization?: string; }) { return post('/api/permissions/add_user', data).catch(throwGlobalError); } @@ -36,7 +35,6 @@ export function revokePermissionFromUser(data: { projectKey?: string; login: string; permission: string; - organization?: string; }) { return post('/api/permissions/remove_user', data).catch(throwGlobalError); } @@ -45,7 +43,6 @@ export function grantPermissionToGroup(data: { projectKey?: string; groupName: string; permission: string; - organization?: string; }) { return post('/api/permissions/add_group', data).catch(throwGlobalError); } @@ -54,7 +51,6 @@ export function revokePermissionFromGroup(data: { projectKey?: string; groupName: string; permission: string; - organization?: string; }) { return post('/api/permissions/remove_group', data).catch(throwGlobalError); } @@ -65,11 +61,9 @@ interface GetPermissionTemplatesResponse { permissions: Array<{ key: string; name: string; description: string }>; } -export function getPermissionTemplates( - organization?: string -): Promise<GetPermissionTemplatesResponse> { +export function getPermissionTemplates(): Promise<GetPermissionTemplatesResponse> { const url = '/api/permissions/search_templates'; - return organization ? getJSON(url, { organization }) : getJSON(url); + return getJSON(url); } export function createPermissionTemplate(data: RequestData) { @@ -103,7 +97,6 @@ export function grantTemplatePermissionToUser(data: { templateId: string; login: string; permission: string; - organization?: string; }): Promise<void> { return post('/api/permissions/add_user_to_template', data); } @@ -112,7 +105,6 @@ export function revokeTemplatePermissionFromUser(data: { templateId: string; login: string; permission: string; - organization?: string; }): Promise<void> { return post('/api/permissions/remove_user_from_template', data); } @@ -140,7 +132,6 @@ export function getPermissionsUsersForComponent(data: { projectKey: string; q?: string; permission?: string; - organization?: string; p?: number; ps?: number; }): Promise<{ paging: T.Paging; users: T.PermissionUser[] }> { @@ -154,7 +145,6 @@ export function getPermissionsGroupsForComponent(data: { projectKey: string; q?: string; permission?: string; - organization?: string; p?: number; ps?: number; }): Promise<{ paging: T.Paging; groups: T.PermissionGroup[] }> { @@ -167,7 +157,6 @@ export function getPermissionsGroupsForComponent(data: { export function getGlobalPermissionsUsers(data: { q?: string; permission?: string; - organization?: string; p?: number; ps?: number; }): Promise<{ paging: T.Paging; users: T.PermissionUser[] }> { @@ -180,7 +169,6 @@ export function getGlobalPermissionsUsers(data: { export function getGlobalPermissionsGroups(data: { q?: string; permission?: string; - organization?: string; p?: number; ps?: number; }): Promise<{ paging: T.Paging; groups: T.PermissionGroup[] }> { @@ -193,8 +181,7 @@ export function getGlobalPermissionsGroups(data: { export function getPermissionTemplateUsers( templateId: string, query?: string, - permission?: string, - organization?: string + permission?: string ): Promise<any> { const data: RequestData = { templateId, ps: PAGE_SIZE }; if (query) { @@ -203,17 +190,13 @@ export function getPermissionTemplateUsers( if (permission) { data.permission = permission; } - if (organization) { - Object.assign(data, { organization }); - } return getJSON('/api/permissions/template_users', data).then(r => r.users); } export function getPermissionTemplateGroups( templateId: string, query?: string, - permission?: string, - organization?: string + permission?: string ): Promise<any> { const data: RequestData = { templateId, ps: PAGE_SIZE }; if (query) { @@ -222,9 +205,6 @@ export function getPermissionTemplateGroups( if (permission) { data.permission = permission; } - if (organization) { - Object.assign(data, { organization }); - } return getJSON('/api/permissions/template_groups', data).then(r => r.groups); } diff --git a/server/sonar-web/src/main/js/api/user_groups.ts b/server/sonar-web/src/main/js/api/user_groups.ts index fca0be2e46d..6d7aa683c93 100644 --- a/server/sonar-web/src/main/js/api/user_groups.ts +++ b/server/sonar-web/src/main/js/api/user_groups.ts @@ -22,7 +22,6 @@ import throwGlobalError from '../app/utils/throwGlobalError'; export function searchUsersGroups(data: { f?: string; - organization?: string; p?: number; ps?: number; q?: string; @@ -33,7 +32,6 @@ export function searchUsersGroups(data: { export function getUsersInGroup(data: { id?: number; name?: string; - organization?: string; p?: number; ps?: number; q?: string; @@ -42,29 +40,15 @@ export function getUsersInGroup(data: { return getJSON('/api/user_groups/users', data).catch(throwGlobalError); } -export function addUserToGroup(data: { - id?: string; - name?: string; - login?: string; - organization?: string; -}) { +export function addUserToGroup(data: { id?: string; name?: string; login?: string }) { return post('/api/user_groups/add_user', data).catch(throwGlobalError); } -export function removeUserFromGroup(data: { - id?: string; - name?: string; - login?: string; - organization?: string; -}) { +export function removeUserFromGroup(data: { id?: string; name?: string; login?: string }) { return post('/api/user_groups/remove_user', data).catch(throwGlobalError); } -export function createGroup(data: { - description?: string; - organization?: string; - name: string; -}): Promise<T.Group> { +export function createGroup(data: { description?: string; name: string }): Promise<T.Group> { return postJSON('/api/user_groups/create', data).then(r => r.group, throwGlobalError); } @@ -72,6 +56,6 @@ export function updateGroup(data: { description?: string; id: number; name?: str return post('/api/user_groups/update', data).catch(throwGlobalError); } -export function deleteGroup(data: { name: string; organization?: string }) { +export function deleteGroup(data: { name: string }) { return post('/api/user_groups/delete', data).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/api/users.ts b/server/sonar-web/src/main/js/api/users.ts index 44bba9b96fc..0fd0364dc00 100644 --- a/server/sonar-web/src/main/js/api/users.ts +++ b/server/sonar-web/src/main/js/api/users.ts @@ -42,7 +42,6 @@ export interface UserGroup { export function getUserGroups(data: { login: string; - organization?: string; p?: number; ps?: number; q?: string; @@ -91,10 +90,6 @@ export function deactivateUser(data: { login: string }): Promise<T.User> { return postJSON('/api/users/deactivate', data).catch(throwGlobalError); } -export function skipOnboarding(): Promise<void | Response> { - return post('/api/users/skip_onboarding_tutorial').catch(throwGlobalError); -} - export function setHomePage(homepage: T.HomePage): Promise<void | Response> { return post('/api/users/set_homepage', homepage).catch(throwGlobalError); } diff --git a/server/sonar-web/src/main/js/app/components/StartupModal.tsx b/server/sonar-web/src/main/js/app/components/StartupModal.tsx index 9d4d41c994f..a873cd4a3ae 100644 --- a/server/sonar-web/src/main/js/app/components/StartupModal.tsx +++ b/server/sonar-web/src/main/js/app/components/StartupModal.tsx @@ -28,7 +28,6 @@ import { showLicense } from '../../api/marketplace'; import { Location, Router, withRouter } from '../../components/hoc/withRouter'; import { isLoggedIn } from '../../helpers/users'; import { getAppState, getCurrentUser, Store } from '../../store/rootReducer'; -import { skipOnboarding } from '../../store/users'; import { EditionKey } from '../../types/editions'; const LicensePromptModal = lazyLoadComponent( @@ -42,10 +41,6 @@ interface StateProps { currentUser: T.CurrentUser; } -interface DispatchProps { - skipOnboarding: () => void; -} - interface OwnProps { children?: React.ReactNode; } @@ -55,7 +50,7 @@ interface WithRouterProps { router: Pick<Router, 'push'>; } -type Props = StateProps & DispatchProps & OwnProps & WithRouterProps; +type Props = StateProps & OwnProps & WithRouterProps; interface State { open?: boolean; @@ -112,6 +107,4 @@ const mapStateToProps = (state: Store): StateProps => ({ currentUser: getCurrentUser(state) }); -const mapDispatchToProps: DispatchProps = { skipOnboarding }; - -export default connect(mapStateToProps, mapDispatchToProps)(withRouter(StartupModal)); +export default connect(mapStateToProps)(withRouter(StartupModal)); diff --git a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx index b97e7e10728..c1aa981c501 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx @@ -130,7 +130,6 @@ function getWrapper(props: Partial<StartupModal['props']> = {}) { currentUser={LOGGED_IN_USER} location={{ pathname: 'foo/bar' }} router={{ push: jest.fn() }} - skipOnboarding={jest.fn()} {...props}> <div /> </StartupModal> diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx index 6da08f37d7b..f384811b69e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx @@ -24,9 +24,8 @@ import Dropdown from 'sonar-ui-common/components/controls/Dropdown'; import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { isMySet } from '../../../../apps/issues/utils'; -import { isSonarCloud } from '../../../../helpers/system'; import { getQualityGatesUrl } from '../../../../helpers/urls'; -import { isLoggedIn } from '../../../../helpers/users'; +import { ComponentQualifier } from '../../../../types/component'; interface Props { appState: Pick<T.AppState, 'canAdmin' | 'globalPages' | 'organizationsEnabled' | 'qualifiers'>; @@ -36,10 +35,6 @@ interface Props { export default class GlobalNavMenu extends React.PureComponent<Props> { renderProjects() { - if (isSonarCloud() && !isLoggedIn(this.props.currentUser)) { - return null; - } - const active = this.props.location.pathname.startsWith('/projects') && this.props.location.pathname !== '/projects/create'; @@ -47,7 +42,7 @@ export default class GlobalNavMenu extends React.PureComponent<Props> { return ( <li> <Link className={classNames({ active })} to="/projects"> - {isSonarCloud() ? translate('my_projects') : translate('projects.page')} + {translate('projects.page')} </Link> </li> ); @@ -64,24 +59,8 @@ export default class GlobalNavMenu extends React.PureComponent<Props> { } renderIssuesLink() { - if (isSonarCloud() && !isLoggedIn(this.props.currentUser)) { - return null; - } - const active = this.props.location.pathname.startsWith('/issues'); - if (isSonarCloud()) { - return ( - <li> - <Link - className={classNames({ active })} - to={{ pathname: '/issues', query: { resolved: 'false' } }}> - {translate('my_issues')} - </Link> - </li> - ); - } - const query = this.props.currentUser.isLoggedIn && isMySet() ? { resolved: 'false', myIssues: 'true' } @@ -174,7 +153,9 @@ export default class GlobalNavMenu extends React.PureComponent<Props> { } render() { - const governanceInstalled = this.props.appState.qualifiers.includes('VW'); + const governanceInstalled = this.props.appState.qualifiers.includes( + ComponentQualifier.Portfolio + ); const { organizationsEnabled } = this.props.appState; return ( diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx index d02c73189b8..bdaf8c4249f 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx @@ -30,12 +30,12 @@ import { updatePermissionTemplate } from '../../../api/permissions'; import { Router, withRouter } from '../../../components/hoc/withRouter'; +import { PERMISSION_TEMPLATES_PATH } from '../utils'; import DeleteForm from './DeleteForm'; import Form from './Form'; interface Props { fromDetails?: boolean; - organization?: { isDefault?: boolean; key: string }; permissionTemplate: T.PermissionTemplate; refresh: () => void; router: Pick<Router, 'replace'>; @@ -91,10 +91,7 @@ export class ActionsCell extends React.PureComponent<Props, State> { handleDeleteSubmit = () => { return deletePermissionTemplate({ templateId: this.props.permissionTemplate.id }).then(() => { - const pathname = this.props.organization - ? `/organizations/${this.props.organization.key}/permission_templates` - : '/permission_templates'; - this.props.router.replace(pathname); + this.props.router.replace(PERMISSION_TEMPLATES_PATH); this.props.refresh(); }); }; @@ -107,11 +104,7 @@ export class ActionsCell extends React.PureComponent<Props, State> { }; getAvailableQualifiers() { - const topQualifiers = - this.props.organization && !this.props.organization.isDefault - ? ['TRK'] - : this.props.topQualifiers; - return difference(topQualifiers, this.props.permissionTemplate.defaultFor); + return difference(this.props.topQualifiers, this.props.permissionTemplate.defaultFor); } renderSetDefaultsControl() { @@ -160,11 +153,7 @@ export class ActionsCell extends React.PureComponent<Props, State> { } render() { - const { permissionTemplate: t, organization } = this.props; - - const pathname = organization - ? `/organizations/${organization.key}/permission_templates` - : '/permission_templates'; + const { permissionTemplate: t } = this.props; return ( <> @@ -172,7 +161,7 @@ export class ActionsCell extends React.PureComponent<Props, State> { {this.renderSetDefaultsControl()} {!this.props.fromDetails && ( - <ActionsDropdownItem to={{ pathname, query: { id: t.id } }}> + <ActionsDropdownItem to={{ pathname: PERMISSION_TEMPLATES_PATH, query: { id: t.id } }}> {translate('edit_permissions')} </ActionsDropdownItem> )} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx index e05c6178687..3a47ab64959 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx @@ -19,11 +19,11 @@ */ import { Location } from 'history'; import * as React from 'react'; +import { Helmet } from 'react-helmet-async'; import { connect } from 'react-redux'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { getPermissionTemplates } from '../../../api/permissions'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; -import OrganizationHelmet from '../../../components/common/OrganizationHelmet'; import { getAppState, Store } from '../../../store/rootReducer'; import '../../permissions/styles.css'; import { mergeDefaultsToTemplates, mergePermissionsToTemplates, sortPermissions } from '../utils'; @@ -32,7 +32,6 @@ import Template from './Template'; interface Props { location: Location; - organization: T.Organization | undefined; topQualifiers: string[]; } @@ -59,21 +58,21 @@ export class App extends React.PureComponent<Props, State> { this.mounted = false; } - requestPermissions = () => { - const { organization } = this.props; - const request = organization - ? getPermissionTemplates(organization.key) - : getPermissionTemplates(); - return request.then(r => { - if (this.mounted) { - const permissions = sortPermissions(r.permissions); - const permissionTemplates = mergeDefaultsToTemplates( - mergePermissionsToTemplates(r.permissionTemplates, permissions), - r.defaultTemplates - ); - this.setState({ ready: true, permissionTemplates, permissions }); - } - }); + requestPermissions = async () => { + const { permissions, defaultTemplates, permissionTemplates } = await getPermissionTemplates(); + + if (this.mounted) { + const sortedPerm = sortPermissions(permissions); + const permissionTemplatesMerged = mergeDefaultsToTemplates( + mergePermissionsToTemplates(permissionTemplates, sortedPerm), + defaultTemplates + ); + this.setState({ + ready: true, + permissionTemplates: permissionTemplatesMerged, + permissions: sortedPerm + }); + } }; renderTemplate(id: string) { @@ -88,7 +87,6 @@ export class App extends React.PureComponent<Props, State> { return ( <Template - organization={this.props.organization} refresh={this.requestPermissions} template={template} topQualifiers={this.props.topQualifiers} @@ -99,7 +97,6 @@ export class App extends React.PureComponent<Props, State> { renderHome() { return ( <Home - organization={this.props.organization} permissionTemplates={this.state.permissionTemplates} permissions={this.state.permissions} ready={this.state.ready} @@ -114,10 +111,7 @@ export class App extends React.PureComponent<Props, State> { return ( <div> <Suggestions suggestions="permission_templates" /> - <OrganizationHelmet - organization={this.props.organization} - title={translate('permission_templates.page')} - /> + <Helmet defer={false} title={translate('permission_templates.page')} /> {id && this.renderTemplate(id)} {!id && this.renderHome()} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.tsx index c03733a57a6..9118e8d577f 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.tsx @@ -22,13 +22,11 @@ import * as React from 'react'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; interface Props { - organization: T.Organization | undefined; template: T.PermissionTemplate; } -export default function Defaults({ organization, template }: Props) { - const qualifiersToDisplay = - organization && !organization.isDefault ? ['TRK'] : template.defaultFor; +export default function Defaults({ template }: Props) { + const qualifiersToDisplay = template.defaultFor; const qualifiers = sortBy(qualifiersToDisplay) .map(qualifier => translate('qualifiers', qualifier)) diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx index 176976188a3..7353ba323fa 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx @@ -22,10 +22,10 @@ import { Button } from 'sonar-ui-common/components/controls/buttons'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { createPermissionTemplate } from '../../../api/permissions'; import { Router, withRouter } from '../../../components/hoc/withRouter'; +import { PERMISSION_TEMPLATES_PATH } from '../utils'; import Form from './Form'; interface Props { - organization?: { key: string }; ready?: boolean; refresh: () => Promise<void>; router: Pick<Router, 'push'>; @@ -62,13 +62,12 @@ class Header extends React.PureComponent<Props, State> { name: string; projectKeyPattern: string; }) => { - const organization = this.props.organization && this.props.organization.key; - return createPermissionTemplate({ ...data, organization }).then(response => { + return createPermissionTemplate({ ...data }).then(response => { this.props.refresh().then(() => { - const pathname = organization - ? `/organizations/${organization}/permission_templates` - : '/permission_templates'; - this.props.router.push({ pathname, query: { id: response.permissionTemplate.id } }); + this.props.router.push({ + pathname: PERMISSION_TEMPLATES_PATH, + query: { id: response.permissionTemplate.id } + }); }); }); }; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx index cd06304d915..3efacf919a1 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx @@ -24,7 +24,6 @@ import Header from './Header'; import List from './List'; interface Props { - organization: T.Organization | undefined; permissionTemplates: T.PermissionTemplate[]; permissions: T.Permission[]; ready: boolean; @@ -37,10 +36,9 @@ export default function Home(props: Props) { <div className="page page-limited"> <Helmet defer={false} title={translate('permission_templates.page')} /> - <Header organization={props.organization} ready={props.ready} refresh={props.refresh} /> + <Header ready={props.ready} refresh={props.refresh} /> <List - organization={props.organization} permissionTemplates={props.permissionTemplates} permissions={props.permissions} refresh={props.refresh} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx index 08b40e89e7b..9579b47ed1a 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx @@ -22,7 +22,6 @@ import ListHeader from './ListHeader'; import ListItem from './ListItem'; interface Props { - organization: T.Organization | undefined; permissionTemplates: T.PermissionTemplate[]; permissions: T.Permission[]; refresh: () => Promise<void>; @@ -31,19 +30,13 @@ interface Props { export default function List(props: Props) { const permissionTemplates = props.permissionTemplates.map(p => ( - <ListItem - key={p.id} - organization={props.organization} - refresh={props.refresh} - template={p} - topQualifiers={props.topQualifiers} - /> + <ListItem key={p.id} refresh={props.refresh} template={p} topQualifiers={props.topQualifiers} /> )); return ( <div className="boxed-group boxed-group-inner"> <table className="data zebra permissions-table" id="permission-templates"> - <ListHeader organization={props.organization} permissions={props.permissions} /> + <ListHeader permissions={props.permissions} /> <tbody>{permissionTemplates}</tbody> </table> </div> diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx index 912f76f000a..6945d82843e 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx @@ -24,7 +24,6 @@ import { translate } from 'sonar-ui-common/helpers/l10n'; import InstanceMessage from '../../../components/common/InstanceMessage'; interface Props { - organization: T.Organization | undefined; permissions: T.Permission[]; } diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx index 68a7e21c7af..ab1eac8b252 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx @@ -23,7 +23,6 @@ import NameCell from './NameCell'; import PermissionCell from './PermissionCell'; interface Props { - organization: T.Organization | undefined; refresh: () => Promise<void>; template: T.PermissionTemplate; topQualifiers: string[]; @@ -36,13 +35,12 @@ export default function ListItem(props: Props) { return ( <tr data-id={props.template.id} data-name={props.template.name}> - <NameCell organization={props.organization} template={props.template} /> + <NameCell template={props.template} /> {permissions} <td className="nowrap thin text-right text-top little-padded-left little-padded-right"> <ActionsCell - organization={props.organization} permissionTemplate={props.template} refresh={props.refresh} topQualifiers={props.topQualifiers} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx index ca84db31958..f27cb518e63 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx @@ -19,17 +19,15 @@ */ import * as React from 'react'; import { Link } from 'react-router'; +import { PERMISSION_TEMPLATES_PATH } from '../utils'; import Defaults from './Defaults'; interface Props { - organization: T.Organization | undefined; template: T.PermissionTemplate; } -export default function NameCell({ template, organization }: Props) { - const pathname = organization - ? `/organizations/${organization.key}/permission_templates` - : '/permission_templates'; +export default function NameCell({ template }: Props) { + const pathname = PERMISSION_TEMPLATES_PATH; return ( <td className="little-padded-left little-padded-right"> @@ -39,7 +37,7 @@ export default function NameCell({ template, organization }: Props) { {template.defaultFor.length > 0 && ( <div className="spacer-top js-defaults"> - <Defaults organization={organization} template={template} /> + <Defaults template={template} /> </div> )} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx index 5aa316db1c1..4d017be6811 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx @@ -31,7 +31,6 @@ import TemplateDetails from './TemplateDetails'; import TemplateHeader from './TemplateHeader'; interface Props { - organization: T.Organization | undefined; refresh: () => void; template: T.PermissionTemplate; topQualifiers: string[]; @@ -85,12 +84,11 @@ export default class Template extends React.PureComponent<Props, State> { } else { requests.push(Promise.resolve([])); } - - return Promise.all(requests).then(responses => { + return Promise.all(requests).then(([users, groups]) => { if (this.mounted) { this.setState({ - users: responses[0], - groups: responses[1], + users, + groups, loading: false }); } @@ -101,16 +99,14 @@ export default class Template extends React.PureComponent<Props, State> { if (user.login === '<creator>') { return this.handleToggleProjectCreator(user, permission); } - const { template, organization } = this.props; + const { template } = this.props; const hasPermission = user.permissions.includes(permission); - const data: { templateId: string; login: string; permission: string; organization?: string } = { + const data: { templateId: string; login: string; permission: string } = { templateId: template.id, login: user.login, permission }; - if (organization) { - data.organization = organization.key; - } + const request = hasPermission ? api.revokeTemplatePermissionFromUser(data) : api.grantTemplatePermissionToUser(data); @@ -127,16 +123,13 @@ export default class Template extends React.PureComponent<Props, State> { }; handleToggleGroup = (group: T.PermissionGroup, permission: string) => { - const { template, organization } = this.props; + const { template } = this.props; const hasPermission = group.permissions.includes(permission); const data = { templateId: template.id, groupName: group.name, permission }; - if (organization) { - Object.assign(data, { organization: organization.key }); - } const request = hasPermission ? api.revokeTemplatePermissionFromGroup(data) : api.grantTemplatePermissionToGroup(data); @@ -179,7 +172,6 @@ export default class Template extends React.PureComponent<Props, State> { PERMISSIONS_ORDER_FOR_PROJECT_TEMPLATE, 'projects_role' ); - const allUsers = [...this.state.users]; const creatorPermissions = this.props.template.permissions @@ -202,13 +194,12 @@ export default class Template extends React.PureComponent<Props, State> { <TemplateHeader loading={this.state.loading} - organization={this.props.organization} refresh={this.props.refresh} template={this.props.template} topQualifiers={this.props.topQualifiers} /> - <TemplateDetails organization={this.props.organization} template={this.props.template} /> + <TemplateDetails template={this.props.template} /> <HoldersList groups={this.state.groups} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx index 9c319a48995..81c75c40157 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx @@ -21,16 +21,15 @@ import * as React from 'react'; import Defaults from './Defaults'; interface Props { - organization: T.Organization | undefined; template: T.PermissionTemplate; } -export default function TemplateDetails({ organization, template }: Props) { +export default function TemplateDetails({ template }: Props) { return ( <div className="big-spacer-bottom"> {template.defaultFor.length > 0 && ( <div className="spacer-top js-defaults"> - <Defaults organization={organization} template={template} /> + <Defaults template={template} /> </div> )} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx index 8910e956bc9..98c63d57291 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx @@ -20,27 +20,22 @@ import * as React from 'react'; import { Link } from 'react-router'; import { translate } from 'sonar-ui-common/helpers/l10n'; +import { PERMISSION_TEMPLATES_PATH } from '../utils'; import ActionsCell from './ActionsCell'; interface Props { loading: boolean; - organization: T.Organization | undefined; refresh: () => void; template: T.PermissionTemplate; topQualifiers: string[]; } export default function TemplateHeader(props: Props) { - const { template, organization } = props; - - const pathname = organization - ? `/organizations/${organization.key}/permission_templates` - : '/permission_templates'; - + const { template } = props; return ( <header className="page-header" id="project-permissions-header"> <div className="note spacer-bottom"> - <Link className="text-muted" to={pathname}> + <Link className="text-muted" to={PERMISSION_TEMPLATES_PATH}> {translate('permission_templates.page')} </Link> </div> @@ -52,7 +47,6 @@ export default function TemplateHeader(props: Props) { <div className="pull-right"> <ActionsCell fromDetails={true} - organization={organization} permissionTemplate={template} refresh={props.refresh} topQualifiers={props.topQualifiers} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.tsx index 655c2b43e79..2b80ad76c5b 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.tsx @@ -53,18 +53,3 @@ it('should not set default', () => { const setDefault = renderActionsCell({ permissionTemplate }).find('.js-set-default'); expect(setDefault.length).toBe(0); }); - -it('should display all qualifiers for default organization', () => { - const organization = { isDefault: true, key: 'org' }; - const setDefault = renderActionsCell({ organization }).find('.js-set-default'); - expect(setDefault.length).toBe(2); - expect(setDefault.at(0).prop('data-qualifier')).toBe('TRK'); - expect(setDefault.at(1).prop('data-qualifier')).toBe('VW'); -}); - -it('should display only projects for custom organization', () => { - const organization = { isDefault: false, key: 'org' }; - const setDefault = renderActionsCell({ organization }).find('.js-set-default'); - expect(setDefault.length).toBe(1); - expect(setDefault.at(0).prop('data-qualifier')).toBe('TRK'); -}); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx index 909973fe3bc..c05cc288f50 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { mockLocation, mockOrganization } from '../../../../helpers/testMocks'; +import { mockLocation } from '../../../../helpers/testMocks'; import { App } from '../App'; jest.mock('../../../../api/permissions', () => ({ @@ -54,12 +54,5 @@ it('should render correctly', async () => { }); function shallowRender(props: Partial<App['props']> = {}) { - return shallow( - <App - location={mockLocation()} - organization={mockOrganization()} - topQualifiers={['TRK']} - {...props} - /> - ); + return shallow(<App location={mockLocation()} topQualifiers={['TRK']} {...props} />); } diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.tsx index 589ad9773a1..c9f910c33f3 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.tsx @@ -31,26 +31,12 @@ const SAMPLE: T.PermissionTemplate = { it('should render one qualifier', () => { const sample = { ...SAMPLE, defaultFor: ['DEV'] }; - const output = shallow(<Defaults organization={undefined} template={sample} />); + const output = shallow(<Defaults template={sample} />); expect(output).toMatchSnapshot(); }); it('should render several qualifiers', () => { const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; - const output = shallow(<Defaults organization={undefined} template={sample} />); - expect(output).toMatchSnapshot(); -}); - -it('should render several qualifiers for default organization', () => { - const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; - const organization: T.Organization = { isDefault: true, key: 'org', name: 'org' }; - const output = shallow(<Defaults organization={organization} template={sample} />); - expect(output).toMatchSnapshot(); -}); - -it('should render only projects for custom organization', () => { - const sample = { ...SAMPLE, defaultFor: ['TRK', 'VW'] }; - const organization: T.Organization = { isDefault: false, key: 'org', name: 'org' }; - const output = shallow(<Defaults organization={organization} template={sample} />); + const output = shallow(<Defaults template={sample} />); expect(output).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ListItem-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ListItem-test.tsx index ee94600fb36..b86f8b57798 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ListItem-test.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ListItem-test.tsx @@ -29,7 +29,6 @@ function shallowRender() { return shallow( <ListItem key="1" - organization={undefined} refresh={async () => {}} template={{ id: '1', diff --git a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/NameCell-test.tsx index 9ba732fa1ae..aca2f187cd2 100644 --- a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/NameCell-test.tsx @@ -17,17 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +import { shallow } from 'enzyme'; import * as React from 'react'; -import { Helmet } from 'react-helmet-async'; +import NameCell from '../NameCell'; -interface Props { - organization?: { name: string }; - title: string; -} +it('render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); -export default function OrganizationHelmet({ title, organization }: Props) { - const defaultTitle = title + (organization ? ' - ' + organization.name : ''); - return ( - <Helmet defaultTitle={defaultTitle} defer={false} titleTemplate={`%s - ${defaultTitle}`} /> +function shallowRender() { + return shallow( + <NameCell + template={{ + id: '1', + createdAt: '2020-01-01', + name: 'test', + defaultFor: ['user'], + permissions: [] + }} + /> ); } diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Template-test.tsx b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Template-test.tsx new file mode 100644 index 00000000000..fb734c32679 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Template-test.tsx @@ -0,0 +1,127 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import { + grantTemplatePermissionToGroup, + grantTemplatePermissionToUser, + revokeTemplatePermissionFromGroup, + revokeTemplatePermissionFromUser +} from '../../../../api/permissions'; +import Template from '../Template'; + +jest.mock('../../../../api/permissions', () => ({ + revokeTemplatePermissionFromUser: jest.fn().mockResolvedValue({}), + grantTemplatePermissionToUser: jest.fn().mockResolvedValue({}), + grantTemplatePermissionToGroup: jest.fn().mockResolvedValue({}), + revokeTemplatePermissionFromGroup: jest.fn().mockResolvedValue({}), + getPermissionTemplateGroups: jest.fn().mockResolvedValue([]), + getPermissionTemplateUsers: jest.fn().mockResolvedValue([]) +})); + +it('render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('revoke group permission if granted', async () => { + const wrapper = shallowRender(); + const group = { + name: 'foo', + permissions: ['bar'] + }; + wrapper.setState({ + groups: [group] + }); + await wrapper.instance().handleToggleGroup(group, 'bar'); + expect(revokeTemplatePermissionFromGroup).toHaveBeenCalledWith({ + groupName: 'foo', + templateId: '1', + permission: 'bar' + }); +}); + +it('grant group permission', async () => { + const wrapper = shallowRender(); + const group = { + name: 'foo', + permissions: [] + }; + wrapper.setState({ + groups: [group] + }); + await wrapper.instance().handleToggleGroup(group, 'bar'); + expect(grantTemplatePermissionToGroup).toHaveBeenCalledWith({ + groupName: 'foo', + templateId: '1', + permission: 'bar' + }); +}); + +it('revoke user permission if granted', async () => { + const wrapper = shallowRender(); + const user = { + login: 'foo', + name: 'foo', + permissions: ['bar'] + }; + wrapper.setState({ + users: [user] + }); + await wrapper.instance().handleToggleUser(user, 'bar'); + expect(revokeTemplatePermissionFromUser).toHaveBeenCalledWith({ + templateId: '1', + login: 'foo', + permission: 'bar' + }); +}); + +it('grant user permission', async () => { + const wrapper = shallowRender(); + const user = { + login: 'foo', + name: 'foo', + permissions: [] + }; + wrapper.setState({ + users: [user] + }); + await wrapper.instance().handleToggleUser(user, 'bar'); + expect(grantTemplatePermissionToUser).toHaveBeenCalledWith({ + templateId: '1', + login: 'foo', + permission: 'bar' + }); +}); + +function shallowRender() { + return shallow<Template>( + <Template + refresh={async () => {}} + template={{ + id: '1', + createdAt: '2020-01-01', + name: 'test', + defaultFor: [], + permissions: [] + }} + topQualifiers={[]} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/App-test.tsx.snap index 327c913c7f0..0c79d187b7b 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/App-test.tsx.snap @@ -5,22 +5,12 @@ exports[`should render correctly 1`] = ` <Suggestions suggestions="permission_templates" /> - <OrganizationHelmet - organization={ - Object { - "key": "foo", - "name": "Foo", - } - } + <Helmet + defer={false} + encodeSpecialCharacters={true} title="permission_templates.page" /> <Home - organization={ - Object { - "key": "foo", - "name": "Foo", - } - } permissionTemplates={Array []} permissions={Array []} ready={false} @@ -39,22 +29,12 @@ exports[`should render correctly 2`] = ` <Suggestions suggestions="permission_templates" /> - <OrganizationHelmet - organization={ - Object { - "key": "foo", - "name": "Foo", - } - } + <Helmet + defer={false} + encodeSpecialCharacters={true} title="permission_templates.page" /> <Home - organization={ - Object { - "key": "foo", - "name": "Foo", - } - } permissionTemplates={ Array [ Object { diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap index 9319abafef2..2713b366b61 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap @@ -10,16 +10,6 @@ exports[`should render one qualifier 1`] = ` </div> `; -exports[`should render only projects for custom organization 1`] = ` -<div> - <span - className="badge spacer-right" - > - permission_template.default_for.qualifiers.TRK - </span> -</div> -`; - exports[`should render several qualifiers 1`] = ` <div> <span @@ -29,13 +19,3 @@ exports[`should render several qualifiers 1`] = ` </span> </div> `; - -exports[`should render several qualifiers for default organization 1`] = ` -<div> - <span - className="badge spacer-right" - > - permission_template.default_for.qualifiers.TRK, qualifiers.VW - </span> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/NameCell-test.tsx.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/NameCell-test.tsx.snap new file mode 100644 index 00000000000..66c34bcc15b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/NameCell-test.tsx.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`render correctly 1`] = ` +<td + className="little-padded-left little-padded-right" +> + <Link + onlyActiveOnIndex={false} + style={Object {}} + to={ + Object { + "pathname": "/permission_templates", + "query": Object { + "id": "1", + }, + } + } + > + <strong + className="js-name" + > + test + </strong> + </Link> + <div + className="spacer-top js-defaults" + > + <Defaults + template={ + Object { + "createdAt": "2020-01-01", + "defaultFor": Array [ + "user", + ], + "id": "1", + "name": "test", + "permissions": Array [], + } + } + /> + </div> +</td> +`; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Template-test.tsx.snap b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Template-test.tsx.snap new file mode 100644 index 00000000000..50cf1bbc3a8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Template-test.tsx.snap @@ -0,0 +1,95 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`render correctly 1`] = ` +<div + className="page page-limited" +> + <Helmet + defer={false} + encodeSpecialCharacters={true} + title="test" + /> + <TemplateHeader + loading={true} + refresh={[Function]} + template={ + Object { + "createdAt": "2020-01-01", + "defaultFor": Array [], + "id": "1", + "name": "test", + "permissions": Array [], + } + } + topQualifiers={Array []} + /> + <TemplateDetails + template={ + Object { + "createdAt": "2020-01-01", + "defaultFor": Array [], + "id": "1", + "name": "test", + "permissions": Array [], + } + } + /> + <HoldersList + groups={Array []} + onSelectPermission={[Function]} + onToggleGroup={[Function]} + onToggleUser={[Function]} + permissions={ + Array [ + Object { + "description": "projects_role.user.desc", + "key": "user", + "name": "projects_role.user", + }, + Object { + "description": "projects_role.codeviewer.desc", + "key": "codeviewer", + "name": "projects_role.codeviewer", + }, + Object { + "description": "projects_role.issueadmin.desc", + "key": "issueadmin", + "name": "projects_role.issueadmin", + }, + Object { + "description": "projects_role.securityhotspotadmin.desc", + "key": "securityhotspotadmin", + "name": "projects_role.securityhotspotadmin", + }, + Object { + "description": "projects_role.admin.desc", + "key": "admin", + "name": "projects_role.admin", + }, + Object { + "description": "projects_role.scan.desc", + "key": "scan", + "name": "projects_role.scan", + }, + ] + } + showPublicProjectsWarning={true} + users={ + Array [ + Object { + "login": "<creator>", + "name": "permission_templates.project_creators", + "permissions": Array [], + }, + ] + } + > + <SearchForm + filter="all" + onFilter={[Function]} + onSearch={[Function]} + query="" + /> + </HoldersList> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/permission-templates/utils.ts b/server/sonar-web/src/main/js/apps/permission-templates/utils.ts index afeacd75895..c305d4a9d7f 100644 --- a/server/sonar-web/src/main/js/apps/permission-templates/utils.ts +++ b/server/sonar-web/src/main/js/apps/permission-templates/utils.ts @@ -28,6 +28,8 @@ export const PERMISSIONS_ORDER = [ 'scan' ]; +export const PERMISSION_TEMPLATES_PATH = '/permission_templates'; + export function sortPermissions(permissions: T.Permission[]) { return sortBy(permissions, p => PERMISSIONS_ORDER.indexOf(p.key)); } diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx index b8bbb81ae98..fcb97f36395 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx @@ -27,10 +27,6 @@ import '../../styles.css'; import AllHoldersList from './AllHoldersList'; import PageHeader from './PageHeader'; -interface Props { - organization?: T.Organization; -} - interface State { filter: 'all' | 'groups' | 'users'; groups: T.PermissionGroup[]; @@ -41,10 +37,10 @@ interface State { usersPaging?: T.Paging; } -export default class App extends React.PureComponent<Props, State> { +export default class App extends React.PureComponent<{}, State> { mounted = false; - constructor(props: Props) { + constructor(props: {}) { super(props); this.state = { filter: 'all', @@ -65,14 +61,12 @@ export default class App extends React.PureComponent<Props, State> { } loadUsersAndGroups = (userPage?: number, groupsPage?: number) => { - const { organization } = this.props; const { filter, query } = this.state; const getUsers: Promise<{ paging?: T.Paging; users: T.PermissionUser[] }> = filter !== 'groups' ? api.getGlobalPermissionsUsers({ q: query || undefined, - organization: organization && organization.key, p: userPage }) : Promise.resolve({ paging: undefined, users: [] }); @@ -81,7 +75,6 @@ export default class App extends React.PureComponent<Props, State> { filter !== 'users' ? api.getGlobalPermissionsGroups({ q: query || undefined, - organization: organization && organization.key, p: groupsPage }) : Promise.resolve({ paging: undefined, groups: [] }); @@ -171,8 +164,7 @@ export default class App extends React.PureComponent<Props, State> { return api .grantPermissionToGroup({ groupName: group, - permission, - organization: this.props.organization && this.props.organization.key + permission }) .then( () => {}, @@ -196,8 +188,7 @@ export default class App extends React.PureComponent<Props, State> { return api .grantPermissionToUser({ login: user, - permission, - organization: this.props.organization && this.props.organization.key + permission }) .then( () => {}, @@ -221,8 +212,7 @@ export default class App extends React.PureComponent<Props, State> { return api .revokePermissionFromGroup({ groupName: group, - permission, - organization: this.props.organization && this.props.organization.key + permission }) .then( () => {}, @@ -246,8 +236,7 @@ export default class App extends React.PureComponent<Props, State> { return api .revokePermissionFromUser({ login: user, - permission, - organization: this.props.organization && this.props.organization.key + permission }) .then( () => {}, @@ -274,7 +263,7 @@ export default class App extends React.PureComponent<Props, State> { <div className="page page-limited"> <Suggestions suggestions="global_permissions" /> <Helmet defer={false} title={translate('global_permissions.permission')} /> - <PageHeader loading={this.state.loading} organization={this.props.organization} /> + <PageHeader loading={this.state.loading} /> <AllHoldersList filter={this.state.filter} grantPermissionToGroup={this.grantPermissionToGroup} diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx index 4be462d184d..bd002dc9dc1 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx @@ -22,26 +22,17 @@ import { translate } from 'sonar-ui-common/helpers/l10n'; interface Props { loading?: boolean; - organization?: {}; } export default class PageHeader extends React.PureComponent<Props> { render() { - const title = this.props.organization - ? translate('permissions.page') - : translate('global_permissions.page'); - - const description = this.props.organization - ? translate('organization_permissions.page.description') - : translate('global_permissions.page.description'); - return ( <header className="page-header"> - <h1 className="page-title">{title}</h1> + <h1 className="page-title">{translate('global_permissions.page')}</h1> {this.props.loading && <i className="spinner" />} - <div className="page-description">{description}</div> + <div className="page-description">{translate('global_permissions.page.description')}</div> </header> ); } diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/App-test.tsx index ddc6dd2037b..f666b0f80fe 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/App-test.tsx @@ -26,7 +26,6 @@ import { revokePermissionFromGroup, revokePermissionFromUser } from '../../../../../api/permissions'; -import { mockOrganization } from '../../../../../helpers/testMocks'; import App from '../App'; jest.mock('../../../../../api/permissions', () => ({ @@ -85,7 +84,7 @@ describe('should manage state correctly', () => { const wrapper = shallowRender(); await waitAndUpdate(wrapper); const instance = wrapper.instance(); - const apiPayload = { groupName: 'Anyone', permission: 'foo', organization: 'foo' }; + const apiPayload = { groupName: 'Anyone', permission: 'foo' }; instance.grantPermissionToGroup('Anyone', 'foo'); const groupState = wrapper.state('groups'); @@ -114,7 +113,7 @@ describe('should manage state correctly', () => { const wrapper = shallowRender(); await waitAndUpdate(wrapper); const instance = wrapper.instance(); - const apiPayload = { login: 'user1', permission: 'foo', organization: 'foo' }; + const apiPayload = { login: 'user1', permission: 'foo' }; instance.grantPermissionToUser('user1', 'foo'); expect(wrapper.state('users')[1].permissions).toHaveLength(1); @@ -130,5 +129,5 @@ describe('should manage state correctly', () => { }); function shallowRender(props: Partial<App['props']> = {}) { - return shallow<App>(<App organization={mockOrganization()} {...props} />); + return shallow<App>(<App {...props} />); } diff --git a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap index a22f405ecc5..589e0cb9e83 100644 --- a/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap @@ -14,12 +14,6 @@ exports[`should render correctly 1`] = ` /> <PageHeader loading={true} - organization={ - Object { - "key": "foo", - "name": "Foo", - } - } /> <Connect(AllHoldersList) filter="all" @@ -53,12 +47,6 @@ exports[`should render correctly 2`] = ` /> <PageHeader loading={false} - organization={ - Object { - "key": "foo", - "name": "Foo", - } - } /> <Connect(AllHoldersList) filter="all" diff --git a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx index 1c7b6d18d68..a207a7ebda0 100644 --- a/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx +++ b/server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx @@ -54,7 +54,7 @@ export default class ApplyTemplate extends React.PureComponent<Props, State> { } fetchPermissionTemplates = () => { - getPermissionTemplates(this.props.organization).then( + getPermissionTemplates().then( ({ permissionTemplates }) => { if (this.mounted) { this.setState({ loading: false, permissionTemplates }); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx index a9f151d9c61..e0eb66f9391 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx @@ -60,7 +60,7 @@ export default class BulkApplyTemplateModal extends React.PureComponent<Props, S loadPermissionTemplates() { this.setState({ loading: true }); - getPermissionTemplates(this.props.organization).then( + getPermissionTemplates().then( ({ permissionTemplates }) => { if (this.mounted) { this.setState({ diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx index fa885a3894e..c4840f52baa 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx @@ -65,8 +65,7 @@ export default class RestoreAccessModal extends React.PureComponent<Props, State grantPermissionToUser({ projectKey: this.props.project.key, login: this.props.currentUser.login, - permission, - organization: this.props.project.organization + permission }); render() { diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx index dc8cad1a84d..0d87f5ac72f 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx @@ -39,7 +39,7 @@ beforeEach(() => { it('fetches permission templates on component mount', () => { shallow(render()); - expect(getPermissionTemplates).toBeCalledWith('org'); + expect(getPermissionTemplates).toBeCalled(); }); it('bulk applies template to all results', async () => { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx index eb267241f69..5ef3289f82a 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx @@ -18,10 +18,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { Helmet } from 'react-helmet-async'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { Actions, getExporters, searchQualityProfiles } from '../../../api/quality-profiles'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; -import OrganizationHelmet from '../../../components/common/OrganizationHelmet'; import '../styles.css'; import { Exporter, Profile } from '../types'; import { sortProfiles } from '../utils'; @@ -109,10 +109,7 @@ export default class App extends React.PureComponent<Props, State> { return ( <div className="page page-limited"> <Suggestions suggestions="quality_profiles" /> - <OrganizationHelmet - organization={this.props.organization} - title={translate('quality_profiles.page')} - /> + <Helmet defer={false} title={translate('quality_profiles.page')} /> {this.renderChild()} </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/App-test.tsx new file mode 100644 index 00000000000..d93c398f7aa --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/App-test.tsx @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import App from '../App'; + +it('should render correctly', () => { + const wrapper = shallowRender(); + expect(wrapper).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<App['props']> = {}) { + return shallow<App>( + <App languages={{}} organization={undefined} {...props}> + <div /> + </App> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/App-test.tsx.snap new file mode 100644 index 00000000000..6fea3f74b16 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/App-test.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="page page-limited" +> + <Suggestions + suggestions="quality_profiles" + /> + <Helmet + defer={false} + encodeSpecialCharacters={true} + title="quality_profiles.page" + /> + <i + className="spinner" + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/store/__tests__/users-test.tsx b/server/sonar-web/src/main/js/store/__tests__/users-test.tsx index b5d5f9eb1d0..d7d634e076a 100644 --- a/server/sonar-web/src/main/js/store/__tests__/users-test.tsx +++ b/server/sonar-web/src/main/js/store/__tests__/users-test.tsx @@ -27,7 +27,6 @@ import reducer, { receiveCurrentUser, setCurrentUserSettingAction, setHomePageAction, - skipOnboardingAction, State } from '../users'; @@ -40,16 +39,6 @@ describe('reducer and actions', () => { expect(newState).toEqual(createState({ currentUser })); }); - it('should allow to skip the onboarding tutorials', () => { - const currentUser = mockLoggedInUser({ showOnboardingTutorial: true }); - const initialState: State = createState({ currentUser }); - - const newState = reducer(initialState, skipOnboardingAction()); - expect(newState).toEqual( - createState({ currentUser: { ...currentUser, showOnboardingTutorial: false } }) - ); - }); - it('should allow to set the homepage', () => { const homepage: T.HomePage = { type: 'PROJECTS' }; const currentUser = mockLoggedInUser({ homepage: undefined }); diff --git a/server/sonar-web/src/main/js/store/users.ts b/server/sonar-web/src/main/js/store/users.ts index 2db0488b2bc..82c116fafd1 100644 --- a/server/sonar-web/src/main/js/store/users.ts +++ b/server/sonar-web/src/main/js/store/users.ts @@ -26,15 +26,13 @@ import { ActionType } from './utils/actions'; const enum Actions { ReceiveCurrentUser = 'RECEIVE_CURRENT_USER', SetCurrentUserSetting = 'SET_CURRENT_USER_SETTING', - SkipOnboardingAction = 'SKIP_ONBOARDING', SetHomePageAction = 'SET_HOMEPAGE' } type Action = | ActionType<typeof receiveCurrentUser, Actions.ReceiveCurrentUser> | ActionType<typeof setCurrentUserSettingAction, Actions.SetCurrentUserSetting> - | ActionType<typeof setHomePageAction, Actions.SetHomePageAction> - | ActionType<typeof skipOnboardingAction, Actions.SkipOnboardingAction>; + | ActionType<typeof setHomePageAction, Actions.SetHomePageAction>; export interface State { usersByLogin: T.Dict<any>; @@ -46,18 +44,6 @@ export function receiveCurrentUser(user: T.CurrentUser) { return { type: Actions.ReceiveCurrentUser, user }; } -export function skipOnboardingAction() { - return { type: Actions.SkipOnboardingAction }; -} - -export function skipOnboarding() { - return (dispatch: Dispatch) => - api.skipOnboarding().then( - () => dispatch(skipOnboardingAction()), - () => dispatch(skipOnboardingAction()) - ); -} - export function setHomePageAction(homepage: T.HomePage) { return { type: Actions.SetHomePageAction, homepage }; } @@ -113,9 +99,6 @@ function currentUser( if (action.type === Actions.ReceiveCurrentUser) { return action.user; } - if (action.type === Actions.SkipOnboardingAction && isLoggedIn(state)) { - return { ...state, showOnboardingTutorial: false } as T.LoggedInUser; - } if (action.type === Actions.SetHomePageAction && isLoggedIn(state)) { return { ...state, homepage: action.homepage } as T.LoggedInUser; } diff --git a/server/sonar-web/src/main/js/types/types.d.ts b/server/sonar-web/src/main/js/types/types.d.ts index e4d8534fee2..56219d6f11c 100644 --- a/server/sonar-web/src/main/js/types/types.d.ts +++ b/server/sonar-web/src/main/js/types/types.d.ts @@ -212,7 +212,6 @@ declare namespace T { export interface CurrentUser { isLoggedIn: boolean; permissions?: { global: string[] }; - showOnboardingTutorial?: boolean; } export interface CurrentUserSetting { |