@@ -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); | |||
} | |||
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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)); |
@@ -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> |
@@ -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 ( |
@@ -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> | |||
)} |
@@ -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()} |
@@ -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)) |
@@ -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 } | |||
}); | |||
}); | |||
}); | |||
}; |
@@ -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} |
@@ -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> |
@@ -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[]; | |||
} | |||
@@ -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} |
@@ -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> | |||
)} | |||
@@ -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} |
@@ -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> | |||
)} | |||
@@ -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} |
@@ -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'); | |||
}); |
@@ -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} />); | |||
} |
@@ -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(); | |||
}); |
@@ -29,7 +29,6 @@ function shallowRender() { | |||
return shallow( | |||
<ListItem | |||
key="1" | |||
organization={undefined} | |||
refresh={async () => {}} | |||
template={{ | |||
id: '1', |
@@ -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: [] | |||
}} | |||
/> | |||
); | |||
} |
@@ -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={[]} | |||
/> | |||
); | |||
} |
@@ -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 { |
@@ -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> | |||
`; |
@@ -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> | |||
`; |
@@ -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> | |||
`; |
@@ -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)); | |||
} |
@@ -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} |
@@ -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> | |||
); | |||
} |
@@ -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} />); | |||
} |
@@ -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" |
@@ -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 }); |
@@ -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({ |
@@ -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() { |
@@ -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 () => { |
@@ -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> |
@@ -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> | |||
); | |||
} |
@@ -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> | |||
`; |
@@ -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 }); |
@@ -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; | |||
} |
@@ -212,7 +212,6 @@ declare namespace T { | |||
export interface CurrentUser { | |||
isLoggedIn: boolean; | |||
permissions?: { global: string[] }; | |||
showOnboardingTutorial?: boolean; | |||
} | |||
export interface CurrentUserSetting { |
@@ -528,7 +528,6 @@ visibility.private.description.long=Only members of the organization will be abl | |||
coding_rules.page=Rules | |||
global_permissions.page=Global Permissions | |||
global_permissions.page.description=Grant and revoke permissions to make changes at the global level. These permissions include editing Quality Profiles, executing analysis, and performing global system administration. | |||
organization_permissions.page.description=Grant and revoke organization permissions. Permissions can be granted to groups or individual users. | |||
roles.page=Project Permissions | |||
roles.page.description2=Grant and revoke project-level permissions. Permissions can be granted to groups or individual users. | |||
roles.page.description_portfolio=Grant and revoke portfolio-level permissions. Permissions can be granted to groups or individual users. |