diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2021-10-08 11:35:12 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-10-22 20:03:27 +0000 |
commit | f1dee48a75bd18996797b269aa08ea3758df083b (patch) | |
tree | ba84125db18ad23e95ef37fa1850025cbb51dd80 /server/sonar-web/src/main/js | |
parent | a1771496e8535a4fe0197690ebbf048a31970472 (diff) | |
download | sonarqube-f1dee48a75bd18996797b269aa08ea3758df083b.tar.gz sonarqube-f1dee48a75bd18996797b269aa08ea3758df083b.zip |
SONAR-15440 Show users with permissions on QG
Diffstat (limited to 'server/sonar-web/src/main/js')
16 files changed, 620 insertions, 99 deletions
diff --git a/server/sonar-web/src/main/js/api/quality-gates.ts b/server/sonar-web/src/main/js/api/quality-gates.ts index 9c33f6b60e0..ae8a2f61c8b 100644 --- a/server/sonar-web/src/main/js/api/quality-gates.ts +++ b/server/sonar-web/src/main/js/api/quality-gates.ts @@ -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); +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx index c37b90a2ff0..ffdc7c0b09f 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.tsx @@ -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)); diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/PermissionItem.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/PermissionItem.tsx new file mode 100644 index 00000000000..94cddb85fa0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/PermissionItem.tsx @@ -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> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissions.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissions.tsx new file mode 100644 index 00000000000..c9541a6a364 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissions.tsx @@ -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} />; + } +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx new file mode 100644 index 00000000000..0e60ff10db8 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatePermissionsRenderer.tsx @@ -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> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DetailsContent-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DetailsContent-test.tsx index 8f8c3c7af2f..1c88e599b0a 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DetailsContent-test.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/DetailsContent-test.tsx @@ -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()} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/PermissionItem-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/PermissionItem-test.tsx new file mode 100644 index 00000000000..cf6fc7c6455 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/PermissionItem-test.tsx @@ -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()} />); +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissions-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissions-test.tsx new file mode 100644 index 00000000000..b89c4a368be --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissions-test.tsx @@ -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} />); +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissionsRenderer-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissionsRenderer-test.tsx new file mode 100644 index 00000000000..6538f6641cb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/QualityGatePermissionsRenderer-test.tsx @@ -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} /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Details-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Details-test.tsx.snap index ba5a76662eb..68f3b08cce1 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Details-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/Details-test.tsx.snap @@ -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]} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap index a8261ab3b2e..1be520d5214 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/DetailsContent-test.tsx.snap @@ -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> `; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap new file mode 100644 index 00000000000..09a811d368b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/PermissionItem-test.tsx.snap @@ -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> +`; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissions-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissions-test.tsx.snap new file mode 100644 index 00000000000..ce01fd4ae76 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissions-test.tsx.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<QualityGatePermissionsRenderer + loading={true} + users={Array []} +/> +`; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsRenderer-test.tsx.snap new file mode 100644 index 00000000000..1e2f83f6744 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/__snapshots__/QualityGatePermissionsRenderer-test.tsx.snap @@ -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> +`; diff --git a/server/sonar-web/src/main/js/types/permissions.ts b/server/sonar-web/src/main/js/types/permissions.ts index 2da4b7d21c3..197ed525c21 100644 --- a/server/sonar-web/src/main/js/types/permissions.ts +++ b/server/sonar-web/src/main/js/types/permissions.ts @@ -21,5 +21,6 @@ export enum Permissions { Admin = 'admin', ProjectCreation = 'provisioning', - ApplicationCreation = 'applicationcreator' + ApplicationCreation = 'applicationcreator', + QualityGateAdmin = 'gateadmin' } diff --git a/server/sonar-web/src/main/js/types/quality-gates.ts b/server/sonar-web/src/main/js/types/quality-gates.ts index 34ec4e75f24..ae17deb3c0a 100644 --- a/server/sonar-web/src/main/js/types/quality-gates.ts +++ b/server/sonar-web/src/main/js/types/quality-gates.ts @@ -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'; +} |