Bläddra i källkod

SONAR-13936 Remove org on user and group API front-end.

tags/8.6.0.39681
Mathieu Suen 3 år sedan
förälder
incheckning
16df95a4fa
44 ändrade filer med 433 tillägg och 366 borttagningar
  1. 4
    24
      server/sonar-web/src/main/js/api/permissions.ts
  2. 4
    20
      server/sonar-web/src/main/js/api/user_groups.ts
  3. 0
    5
      server/sonar-web/src/main/js/api/users.ts
  4. 2
    9
      server/sonar-web/src/main/js/app/components/StartupModal.tsx
  5. 0
    1
      server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx
  6. 5
    24
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx
  7. 5
    16
      server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx
  8. 17
    23
      server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx
  9. 2
    4
      server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.tsx
  10. 6
    7
      server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx
  11. 1
    3
      server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx
  12. 2
    9
      server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx
  13. 0
    1
      server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx
  14. 1
    3
      server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx
  15. 4
    6
      server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx
  16. 8
    17
      server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx
  17. 2
    3
      server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx
  18. 3
    9
      server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx
  19. 0
    15
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.tsx
  20. 2
    9
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx
  21. 2
    16
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.tsx
  22. 0
    1
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ListItem-test.tsx
  23. 17
    9
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/NameCell-test.tsx
  24. 127
    0
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Template-test.tsx
  25. 6
    26
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/App-test.tsx.snap
  26. 0
    20
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap
  27. 43
    0
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/NameCell-test.tsx.snap
  28. 95
    0
      server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Template-test.tsx.snap
  29. 2
    0
      server/sonar-web/src/main/js/apps/permission-templates/utils.ts
  30. 7
    18
      server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx
  31. 2
    11
      server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx
  32. 3
    4
      server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/App-test.tsx
  33. 0
    12
      server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap
  34. 1
    1
      server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx
  35. 1
    1
      server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx
  36. 1
    2
      server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx
  37. 1
    1
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx
  38. 2
    5
      server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx
  39. 35
    0
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/App-test.tsx
  40. 19
    0
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/App-test.tsx.snap
  41. 0
    11
      server/sonar-web/src/main/js/store/__tests__/users-test.tsx
  42. 1
    18
      server/sonar-web/src/main/js/store/users.ts
  43. 0
    1
      server/sonar-web/src/main/js/types/types.d.ts
  44. 0
    1
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 4
- 24
server/sonar-web/src/main/js/api/permissions.ts Visa fil

@@ -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);
}


+ 4
- 20
server/sonar-web/src/main/js/api/user_groups.ts Visa fil

@@ -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);
}

+ 0
- 5
server/sonar-web/src/main/js/api/users.ts Visa fil

@@ -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);
}

+ 2
- 9
server/sonar-web/src/main/js/app/components/StartupModal.tsx Visa fil

@@ -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));

+ 0
- 1
server/sonar-web/src/main/js/app/components/__tests__/StartupModal-test.tsx Visa fil

@@ -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>

+ 5
- 24
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.tsx Visa fil

@@ -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 (

+ 5
- 16
server/sonar-web/src/main/js/apps/permission-templates/components/ActionsCell.tsx Visa fil

@@ -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>
)}

+ 17
- 23
server/sonar-web/src/main/js/apps/permission-templates/components/App.tsx Visa fil

@@ -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()}

+ 2
- 4
server/sonar-web/src/main/js/apps/permission-templates/components/Defaults.tsx Visa fil

@@ -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))

+ 6
- 7
server/sonar-web/src/main/js/apps/permission-templates/components/Header.tsx Visa fil

@@ -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 }
});
});
});
};

+ 1
- 3
server/sonar-web/src/main/js/apps/permission-templates/components/Home.tsx Visa fil

@@ -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}

+ 2
- 9
server/sonar-web/src/main/js/apps/permission-templates/components/List.tsx Visa fil

@@ -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>

+ 0
- 1
server/sonar-web/src/main/js/apps/permission-templates/components/ListHeader.tsx Visa fil

@@ -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[];
}


+ 1
- 3
server/sonar-web/src/main/js/apps/permission-templates/components/ListItem.tsx Visa fil

@@ -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}

+ 4
- 6
server/sonar-web/src/main/js/apps/permission-templates/components/NameCell.tsx Visa fil

@@ -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>
)}


+ 8
- 17
server/sonar-web/src/main/js/apps/permission-templates/components/Template.tsx Visa fil

@@ -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}

+ 2
- 3
server/sonar-web/src/main/js/apps/permission-templates/components/TemplateDetails.tsx Visa fil

@@ -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>
)}


+ 3
- 9
server/sonar-web/src/main/js/apps/permission-templates/components/TemplateHeader.tsx Visa fil

@@ -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}

+ 0
- 15
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ActionsCell-test.tsx Visa fil

@@ -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');
});

+ 2
- 9
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/App-test.tsx Visa fil

@@ -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} />);
}

+ 2
- 16
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Defaults-test.tsx Visa fil

@@ -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();
});

+ 0
- 1
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/ListItem-test.tsx Visa fil

@@ -29,7 +29,6 @@ function shallowRender() {
return shallow(
<ListItem
key="1"
organization={undefined}
refresh={async () => {}}
template={{
id: '1',

server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx → server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/NameCell-test.tsx Visa fil

@@ -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: []
}}
/>
);
}

+ 127
- 0
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/Template-test.tsx Visa fil

@@ -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={[]}
/>
);
}

+ 6
- 26
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/App-test.tsx.snap Visa fil

@@ -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 {

+ 0
- 20
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Defaults-test.tsx.snap Visa fil

@@ -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>
`;

+ 43
- 0
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/NameCell-test.tsx.snap Visa fil

@@ -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>
`;

+ 95
- 0
server/sonar-web/src/main/js/apps/permission-templates/components/__tests__/__snapshots__/Template-test.tsx.snap Visa fil

@@ -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>
`;

+ 2
- 0
server/sonar-web/src/main/js/apps/permission-templates/utils.ts Visa fil

@@ -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));
}

+ 7
- 18
server/sonar-web/src/main/js/apps/permissions/global/components/App.tsx Visa fil

@@ -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}

+ 2
- 11
server/sonar-web/src/main/js/apps/permissions/global/components/PageHeader.tsx Visa fil

@@ -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>
);
}

+ 3
- 4
server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/App-test.tsx Visa fil

@@ -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} />);
}

+ 0
- 12
server/sonar-web/src/main/js/apps/permissions/global/components/__tests__/__snapshots__/App-test.tsx.snap Visa fil

@@ -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"

+ 1
- 1
server/sonar-web/src/main/js/apps/permissions/project/components/ApplyTemplate.tsx Visa fil

@@ -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 });

+ 1
- 1
server/sonar-web/src/main/js/apps/projectsManagement/BulkApplyTemplateModal.tsx Visa fil

@@ -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({

+ 1
- 2
server/sonar-web/src/main/js/apps/projectsManagement/RestoreAccessModal.tsx Visa fil

@@ -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() {

+ 1
- 1
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx Visa fil

@@ -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 () => {

+ 2
- 5
server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx Visa fil

@@ -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>

+ 35
- 0
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/App-test.tsx Visa fil

@@ -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>
);
}

+ 19
- 0
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/App-test.tsx.snap Visa fil

@@ -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>
`;

+ 0
- 11
server/sonar-web/src/main/js/store/__tests__/users-test.tsx Visa fil

@@ -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 });

+ 1
- 18
server/sonar-web/src/main/js/store/users.ts Visa fil

@@ -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;
}

+ 0
- 1
server/sonar-web/src/main/js/types/types.d.ts Visa fil

@@ -212,7 +212,6 @@ declare namespace T {
export interface CurrentUser {
isLoggedIn: boolean;
permissions?: { global: string[] };
showOnboardingTutorial?: boolean;
}

export interface CurrentUserSetting {

+ 0
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties Visa fil

@@ -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.

Laddar…
Avbryt
Spara