Browse Source

SONAR-15440 Show users with permissions on QG

tags/9.2.0.49834
Jeremy Davis 2 years ago
parent
commit
f1dee48a75
17 changed files with 623 additions and 99 deletions
  1. 9
    1
      server/sonar-web/src/main/js/api/quality-gates.ts
  2. 39
    25
      server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx
  3. 40
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/PermissionItem.tsx
  4. 76
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissions.tsx
  5. 50
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx
  6. 5
    1
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DetailsContent-test.tsx
  7. 31
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/PermissionItem-test.tsx
  8. 45
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissions-test.tsx
  9. 36
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissionsRenderer-test.tsx
  10. 1
    1
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Details-test.tsx.snap
  11. 186
    70
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap
  12. 25
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap
  13. 8
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissions-test.tsx.snap
  14. 61
    0
      server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsRenderer-test.tsx.snap
  15. 2
    1
      server/sonar-web/src/main/js/types/permissions.ts
  16. 6
    0
      server/sonar-web/src/main/js/types/quality-gates.ts
  17. 3
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 9
- 1
server/sonar-web/src/main/js/api/quality-gates.ts View File

@@ -20,7 +20,11 @@
import throwGlobalError from '../app/utils/throwGlobalError';
import { getJSON, post, postJSON } from '../helpers/request';
import { BranchParameters } from '../types/branch-like';
import { QualityGateApplicationStatus, QualityGateProjectStatus } from '../types/quality-gates';
import {
QualityGateApplicationStatus,
QualityGateProjectStatus,
SearchPermissionsParameters
} from '../types/quality-gates';

export function fetchQualityGates(): Promise<{
actions: { create: boolean };
@@ -124,3 +128,7 @@ export function getQualityGateProjectStatus(
.then(r => r.projectStatus)
.catch(throwGlobalError);
}

export function searchUsers(data: SearchPermissionsParameters): Promise<{ users: T.UserBase[] }> {
return getJSON('/api/qualitygates/search_users', data).catch(throwGlobalError);
}

+ 39
- 25
server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx View File

@@ -19,12 +19,17 @@
*/
import * as React from 'react';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { withCurrentUser } from '../../../components/hoc/withCurrentUser';
import { Alert } from '../../../components/ui/Alert';
import { translate } from '../../../helpers/l10n';
import { hasGlobalPermission } from '../../../helpers/users';
import { Permissions } from '../../../types/permissions';
import Conditions from './Conditions';
import Projects from './Projects';
import QualityGatePermissions from './QualityGatePermissions';

export interface DetailsContentProps {
currentUser: T.CurrentUser;
isDefault?: boolean;
metrics: T.Dict<T.Metric>;
onAddCondition: (condition: T.Condition) => void;
@@ -35,9 +40,11 @@ export interface DetailsContentProps {
}

export function DetailsContent(props: DetailsContentProps) {
const { isDefault, metrics, qualityGate, updatedConditionId } = props;
const { currentUser, isDefault, metrics, qualityGate, updatedConditionId } = props;
const conditions = qualityGate.conditions || [];
const actions = qualityGate.actions || ({} as any);
const actions = qualityGate.actions || {};

const displayPermissions = hasGlobalPermission(currentUser, Permissions.QualityGateAdmin);

return (
<div className="layout-page-main-inner">
@@ -48,7 +55,7 @@ export function DetailsContent(props: DetailsContentProps) {
)}

<Conditions
canEdit={actions.manageConditions}
canEdit={Boolean(actions.manageConditions)}
conditions={conditions}
metrics={metrics}
onAddCondition={props.onAddCondition}
@@ -58,31 +65,38 @@ export function DetailsContent(props: DetailsContentProps) {
updatedConditionId={updatedConditionId}
/>

<div className="quality-gate-section" id="quality-gate-projects">
<header className="display-flex-center spacer-bottom">
<h3>{translate('quality_gates.projects')}</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div className="big-padded-top big-padded-bottom">
{translate('quality_gates.projects.help')}
</div>
}
/>
</header>
{isDefault ? (
translate('quality_gates.projects_for_default')
) : (
<Projects
canEdit={actions.associateProjects}
// pass unique key to re-mount the component when the quality gate changes
key={qualityGate.id}
qualityGate={qualityGate}
/>
<div className="display-flex-row huge-spacer-top">
<div className="quality-gate-section width-50 big-padded-right" id="quality-gate-projects">
<header className="display-flex-center spacer-bottom">
<h3>{translate('quality_gates.projects')}</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div className="big-padded-top big-padded-bottom">
{translate('quality_gates.projects.help')}
</div>
}
/>
</header>
{isDefault ? (
translate('quality_gates.projects_for_default')
) : (
<Projects
canEdit={actions.associateProjects}
// pass unique key to re-mount the component when the quality gate changes
key={qualityGate.id}
qualityGate={qualityGate}
/>
)}
</div>
{displayPermissions && (
<div className="width-50 big-padded-left">
<QualityGatePermissions qualityGate={qualityGate} />
</div>
)}
</div>
</div>
);
}

export default React.memo(DetailsContent);
export default React.memo(withCurrentUser(DetailsContent));

+ 40
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/PermissionItem.tsx View File

@@ -0,0 +1,40 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 * as React from 'react';
import Avatar from '../../../components/ui/Avatar';

interface Props {
user: T.UserBase;
}

export default function PermissionItem(props: Props) {
const { user } = props;

return (
<div className="display-flex-row">
<Avatar className="spacer-right" hash={user.avatar} name={user.name} size={32} />

<div className="overflow-hidden">
<strong>{user.name}</strong>
<div className="note">{user.login}</div>
</div>
</div>
);
}

+ 76
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissions.tsx View File

@@ -0,0 +1,76 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 * as React from 'react';
import { searchUsers } from '../../../api/quality-gates';
import QualityGatePermissionsRenderer from './QualityGatePermissionsRenderer';

interface Props {
qualityGate: T.QualityGate;
}

interface State {
loading: boolean;
users: T.UserBase[];
}

export default class QualityGatePermissions extends React.Component<Props, State> {
mounted = false;
state: State = {
loading: true,
users: []
};

componentDidMount() {
this.mounted = true;

this.fetchPermissions();
}

componentDidUpdate(newProps: Props) {
if (this.props.qualityGate.id !== newProps.qualityGate.id) {
this.fetchPermissions();
}
}

componentWillUnmount() {
this.mounted = false;
}

fetchPermissions = async () => {
const { qualityGate } = this.props;
this.setState({ loading: true });

const { users } = await searchUsers({
qualityGate: qualityGate.id,
selected: 'selected'
}).catch(() => ({
users: []
}));

if (this.mounted) {
this.setState({ loading: false, users });
}
};

render() {
const { loading, users } = this.state;
return <QualityGatePermissionsRenderer loading={loading} users={users} />;
}
}

+ 50
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx View File

@@ -0,0 +1,50 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 * as React from 'react';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { translate } from '../../../helpers/l10n';
import PermissionItem from './PermissionItem';

export interface QualityGatePermissionsRendererProps {
loading: boolean;
users: T.UserBase[];
}

export default function QualityGatePermissionsRenderer(props: QualityGatePermissionsRendererProps) {
const { loading, users } = props;

return (
<div>
<header className="display-flex-center spacer-bottom">
<h3>{translate('quality_gates.permissions')}</h3>
</header>
<p className="spacer-bottom">{translate('quality_gates.permissions.help')}</p>
<DeferredSpinner loading={loading}>
<ul>
{users.map(user => (
<li key={user.login} className="spacer-top">
<PermissionItem user={user} />
</li>
))}
</ul>
</DeferredSpinner>
</div>
);
}

+ 5
- 1
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DetailsContent-test.tsx View File

@@ -20,7 +20,7 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
import { mockCondition } from '../../../../helpers/testMocks';
import { mockCondition, mockLoggedInUser } from '../../../../helpers/testMocks';
import { DetailsContent, DetailsContentProps } from '../DetailsContent';

it('should render correctly', () => {
@@ -29,11 +29,15 @@ it('should render correctly', () => {
expect(
shallowRender({ isDefault: true, qualityGate: mockQualityGate({ conditions: [] }) })
).toMatchSnapshot('is default, no conditions');
expect(
shallowRender({ currentUser: mockLoggedInUser({ permissions: { global: ['gateadmin'] } }) })
).toMatchSnapshot('Admin');
});

function shallowRender(props: Partial<DetailsContentProps> = {}) {
return shallow(
<DetailsContent
currentUser={mockLoggedInUser()}
metrics={{}}
onAddCondition={jest.fn()}
onRemoveCondition={jest.fn()}

+ 31
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/PermissionItem-test.tsx View File

@@ -0,0 +1,31 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 { mockUser } from '../../../../helpers/testMocks';
import PermissionItem from '../PermissionItem';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender() {
return shallow(<PermissionItem user={mockUser()} />);
}

+ 45
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissions-test.tsx View File

@@ -0,0 +1,45 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 { searchUsers } from '../../../../api/quality-gates';
import { mockQualityGate } from '../../../../helpers/mocks/quality-gates';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import QualityGatePermissions from '../QualityGatePermissions';

jest.mock('../../../../api/quality-gates', () => ({
searchUsers: jest.fn().mockResolvedValue({ users: [] })
}));

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

it('should fetch users', async () => {
const wrapper = shallowRender();

await waitAndUpdate(wrapper);

expect(searchUsers).toBeCalledWith({ qualityGate: '1', selected: 'selected' });
});

function shallowRender(overrides: Partial<QualityGatePermissions['props']> = {}) {
return shallow(<QualityGatePermissions qualityGate={mockQualityGate()} {...overrides} />);
}

+ 36
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissionsRenderer-test.tsx View File

@@ -0,0 +1,36 @@
/*
* SonarQube
* Copyright (C) 2009-2021 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 { mockUser } from '../../../../helpers/testMocks';
import QualityGatePermissionsRenderer, {
QualityGatePermissionsRendererProps
} from '../QualityGatePermissionsRenderer';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot('with users');
expect(shallowRender({ users: [] })).toMatchSnapshot('with no users');
});

function shallowRender(overrides: Partial<QualityGatePermissionsRendererProps> = {}) {
return shallow(
<QualityGatePermissionsRenderer loading={false} users={[mockUser()]} {...overrides} />
);
}

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Details-test.tsx.snap View File

@@ -24,7 +24,7 @@ exports[`should render correctly: loaded 1`] = `
refreshItem={[Function]}
refreshList={[MockFunction]}
/>
<Memo(DetailsContent)
<Memo(Connect(withCurrentUser(DetailsContent)))
isDefault={false}
metrics={Object {}}
onAddCondition={[Function]}

+ 186
- 70
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap View File

@@ -1,10 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: is default 1`] = `
exports[`should render correctly: Admin 1`] = `
<div
className="layout-page-main-inner"
>
<Connect(withAppState(Conditions))
canEdit={false}
conditions={
Array [
Object {
@@ -35,27 +36,132 @@ exports[`should render correctly: is default 1`] = `
}
/>
<div
className="quality-gate-section"
id="quality-gate-projects"
className="display-flex-row huge-spacer-top"
>
<header
className="display-flex-center spacer-bottom"
<div
className="quality-gate-section width-50 big-padded-right"
id="quality-gate-projects"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
<header
className="display-flex-center spacer-bottom"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
}
/>
</header>
<Projects
key="1"
qualityGate={
Object {
"conditions": Array [
Object {
"error": "10",
"id": 1,
"metric": "coverage",
"op": "LT",
},
],
"id": "1",
"name": "qualitygate",
}
}
/>
</header>
quality_gates.projects_for_default
</div>
<div
className="width-50 big-padded-left"
>
<QualityGatePermissions
qualityGate={
Object {
"conditions": Array [
Object {
"error": "10",
"id": 1,
"metric": "coverage",
"op": "LT",
},
],
"id": "1",
"name": "qualitygate",
}
}
/>
</div>
</div>
</div>
`;

exports[`should render correctly: is default 1`] = `
<div
className="layout-page-main-inner"
>
<Connect(withAppState(Conditions))
canEdit={false}
conditions={
Array [
Object {
"error": "10",
"id": 1,
"metric": "coverage",
"op": "LT",
},
]
}
metrics={Object {}}
onAddCondition={[MockFunction]}
onRemoveCondition={[MockFunction]}
onSaveCondition={[MockFunction]}
qualityGate={
Object {
"conditions": Array [
Object {
"error": "10",
"id": 1,
"metric": "coverage",
"op": "LT",
},
],
"id": "1",
"name": "qualitygate",
}
}
/>
<div
className="display-flex-row huge-spacer-top"
>
<div
className="quality-gate-section width-50 big-padded-right"
id="quality-gate-projects"
>
<header
className="display-flex-center spacer-bottom"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
}
/>
</header>
quality_gates.projects_for_default
</div>
</div>
</div>
`;
@@ -71,6 +177,7 @@ exports[`should render correctly: is default, no conditions 1`] = `
quality_gates.is_default_no_conditions
</Alert>
<Connect(withAppState(Conditions))
canEdit={false}
conditions={Array []}
metrics={Object {}}
onAddCondition={[MockFunction]}
@@ -85,27 +192,31 @@ exports[`should render correctly: is default, no conditions 1`] = `
}
/>
<div
className="quality-gate-section"
id="quality-gate-projects"
className="display-flex-row huge-spacer-top"
>
<header
className="display-flex-center spacer-bottom"
<div
className="quality-gate-section width-50 big-padded-right"
id="quality-gate-projects"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
}
/>
</header>
quality_gates.projects_for_default
<header
className="display-flex-center spacer-bottom"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
}
/>
</header>
quality_gates.projects_for_default
</div>
</div>
</div>
`;
@@ -115,6 +226,7 @@ exports[`should render correctly: is not default 1`] = `
className="layout-page-main-inner"
>
<Connect(withAppState(Conditions))
canEdit={false}
conditions={
Array [
Object {
@@ -145,43 +257,47 @@ exports[`should render correctly: is not default 1`] = `
}
/>
<div
className="quality-gate-section"
id="quality-gate-projects"
className="display-flex-row huge-spacer-top"
>
<header
className="display-flex-center spacer-bottom"
<div
className="quality-gate-section width-50 big-padded-right"
id="quality-gate-projects"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
<header
className="display-flex-center spacer-bottom"
>
<h3>
quality_gates.projects
</h3>
<HelpTooltip
className="spacer-left"
overlay={
<div
className="big-padded-top big-padded-bottom"
>
quality_gates.projects.help
</div>
}
/>
</header>
<Projects
key="1"
qualityGate={
Object {
"conditions": Array [
Object {
"error": "10",
"id": 1,
"metric": "coverage",
"op": "LT",
},
],
"id": "1",
"name": "qualitygate",
}
}
/>
</header>
<Projects
key="1"
qualityGate={
Object {
"conditions": Array [
Object {
"error": "10",
"id": 1,
"metric": "coverage",
"op": "LT",
},
],
"id": "1",
"name": "qualitygate",
}
}
/>
</div>
</div>
</div>
`;

+ 25
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap View File

@@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="display-flex-row"
>
<Connect(Avatar)
className="spacer-right"
name="John Doe"
size={32}
/>
<div
className="overflow-hidden"
>
<strong>
John Doe
</strong>
<div
className="note"
>
john.doe
</div>
</div>
</div>
`;

+ 8
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissions-test.tsx.snap View File

@@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<QualityGatePermissionsRenderer
loading={true}
users={Array []}
/>
`;

+ 61
- 0
server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsRenderer-test.tsx.snap View File

@@ -0,0 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly: with no users 1`] = `
<div>
<header
className="display-flex-center spacer-bottom"
>
<h3>
quality_gates.permissions
</h3>
</header>
<p
className="spacer-bottom"
>
quality_gates.permissions.help
</p>
<DeferredSpinner
loading={false}
>
<ul />
</DeferredSpinner>
</div>
`;

exports[`should render correctly: with users 1`] = `
<div>
<header
className="display-flex-center spacer-bottom"
>
<h3>
quality_gates.permissions
</h3>
</header>
<p
className="spacer-bottom"
>
quality_gates.permissions.help
</p>
<DeferredSpinner
loading={false}
>
<ul>
<li
className="spacer-top"
key="john.doe"
>
<PermissionItem
user={
Object {
"active": true,
"local": true,
"login": "john.doe",
"name": "John Doe",
}
}
/>
</li>
</ul>
</DeferredSpinner>
</div>
`;

+ 2
- 1
server/sonar-web/src/main/js/types/permissions.ts View File

@@ -21,5 +21,6 @@
export enum Permissions {
Admin = 'admin',
ProjectCreation = 'provisioning',
ApplicationCreation = 'applicationcreator'
ApplicationCreation = 'applicationcreator',
QualityGateAdmin = 'gateadmin'
}

+ 6
- 0
server/sonar-web/src/main/js/types/quality-gates.ts View File

@@ -80,3 +80,9 @@ export interface QualityGateStatusCondition {
export interface QualityGateStatusConditionEnhanced extends QualityGateStatusCondition {
measure: T.MeasureEnhanced;
}

export interface SearchPermissionsParameters {
qualityGate: string;
q?: string;
selected?: 'all' | 'selected' | 'deselected';
}

+ 3
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -1734,6 +1734,9 @@ quality_gates.built_in.description.1=This quality gate is provided by default.
quality_gates.built_in.description.2=It will automatically be updated with the latest recommendations.
quality_gates.status=Quality Gate status
quality_gates.help=A Quality Gate is a set of measure-based, Boolean conditions. It helps you know immediately whether your projects are production-ready. Ideally, all projects will use the same quality gate. Each project's Quality Gate status is displayed prominently on its homepage.
quality_gates.permissions=Permissions
quality_gates.permissions.help=Users with the global "Manage Quality Gates" permission can manage this Quality Gate.


#------------------------------------------------------------------------------
#

Loading…
Cancel
Save