From e7d3f62c1c161b76230960bc4301eaf7f20f7f1a Mon Sep 17 00:00:00 2001 From: guillaume-peoch-sonarsource Date: Mon, 31 Jul 2023 10:29:58 +0200 Subject: [PATCH] SONAR-19967 Use GET api/v2/users on the FE for Assignees --- .../src/main/js/api/mocks/IssuesServiceMock.ts | 13 ++++++++----- .../js/api/mocks/SecurityHotspotServiceMock.ts | 17 +++++++++-------- server/sonar-web/src/main/js/api/users.ts | 14 -------------- .../apps/issues/components/AssigneeSelect.tsx | 6 +++--- .../sonar-web/src/main/js/apps/issues/utils.ts | 10 +++++----- .../__tests__/SecurityHotspotsApp-it.tsx | 4 ++-- .../security-hotspots/components/Assignee.tsx | 6 +++--- .../components/issue/components/IssueAssign.tsx | 6 +++--- 8 files changed, 33 insertions(+), 43 deletions(-) diff --git a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts index efaf2bd2c70..3ee95b5ef5c 100644 --- a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts @@ -42,7 +42,7 @@ import { MetricKey } from '../../types/metrics'; import { SearchRulesQuery } from '../../types/rules'; import { Standards } from '../../types/security'; import { Dict, Rule, RuleActivation, RuleDetails, SnippetsByComponent } from '../../types/types'; -import { LoggedInUser, NoticeType, User } from '../../types/users'; +import { LoggedInUser, NoticeType, RestUser } from '../../types/users'; import { addIssueComment, bulkChangeIssues, @@ -61,7 +61,7 @@ import { setIssueType, } from '../issues'; import { getRuleDetails, searchRules } from '../rules'; -import { dismissNotice, getCurrentUser, searchUsers } from '../users'; +import { dismissNotice, getCurrentUser, getUsers } from '../users'; import { IssueData, mockIssuesList } from './data/issues'; import { mockRuleList } from './data/rules'; @@ -123,7 +123,7 @@ export default class IssuesServiceMock { jest.mocked(searchIssues).mockImplementation(this.handleSearchIssues); jest.mocked(searchIssueTags).mockImplementation(this.handleSearchIssueTags); jest.mocked(searchRules).mockImplementation(this.handleSearchRules); - jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers); + jest.mocked(getUsers).mockImplementation(this.handleGetUsers); jest.mocked(setIssueAssignee).mockImplementation(this.handleSetIssueAssignee); jest.mocked(setIssueSeverity).mockImplementation(this.handleSetIssueSeverity); jest.mocked(setIssueTags).mockImplementation(this.handleSetIssueTags); @@ -625,8 +625,11 @@ export default class IssuesServiceMock { ); }; - handleSearchUsers = () => { - return this.reply({ paging: mockPaging(), users: [mockLoggedInUser() as unknown as User] }); + handleGetUsers = () => { + return this.reply({ + pageRestResponse: mockPaging(), + users: [mockLoggedInUser() as unknown as RestUser], + }); }; handleSearchIssueAuthors = () => { diff --git a/server/sonar-web/src/main/js/api/mocks/SecurityHotspotServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/SecurityHotspotServiceMock.ts index a2086d31ab7..6124747de14 100644 --- a/server/sonar-web/src/main/js/api/mocks/SecurityHotspotServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/SecurityHotspotServiceMock.ts @@ -27,7 +27,7 @@ import { } from '../../helpers/mocks/security-hotspots'; import { mockSourceLine } from '../../helpers/mocks/sources'; import { getStandards } from '../../helpers/security-standard'; -import { mockPaging, mockUser } from '../../helpers/testMocks'; +import { mockPaging, mockRestUser } from '../../helpers/testMocks'; import { Hotspot, HotspotAssignRequest, @@ -35,6 +35,7 @@ import { HotspotResolution, HotspotStatus, } from '../../types/security-hotspots'; +import { RestUser } from '../../types/users'; import { getSources } from '../components'; import { getMeasures } from '../measures'; import { @@ -47,7 +48,7 @@ import { getSecurityHotspots, setSecurityHotspotStatus, } from '../security-hotspots'; -import { searchUsers } from '../users'; +import { getUsers } from '../users'; const NUMBER_OF_LINES = 20; @@ -66,7 +67,7 @@ export default class SecurityHotspotServiceMock { jest.mocked(getSecurityHotspotList).mockImplementation(this.handleGetSecurityHotspotList); jest.mocked(assignSecurityHotspot).mockImplementation(this.handleAssignSecurityHotspot); jest.mocked(setSecurityHotspotStatus).mockImplementation(this.handleSetSecurityHotspotStatus); - jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers); + jest.mocked(getUsers).mockImplementation((p) => this.handleGetUsers(p)); jest.mocked(getSources).mockResolvedValue( times(NUMBER_OF_LINES, (n) => mockSourceLine({ @@ -119,14 +120,14 @@ export default class SecurityHotspotServiceMock { return Promise.resolve(); }; - handleSearchUsers = () => { + handleGetUsers: typeof getUsers = () => { return this.reply({ users: [ - mockUser({ name: 'User John', login: 'user.john' }), - mockUser({ name: 'User Doe', login: 'user.doe' }), - mockUser({ name: 'User Foo', login: 'user.foo' }), + mockRestUser({ name: 'User John', login: 'user.john' }), + mockRestUser({ name: 'User Doe', login: 'user.doe' }), + mockRestUser({ name: 'User Foo', login: 'user.foo' }), ], - paging: mockPaging(), + pageRestResponse: mockPaging(), }); }; diff --git a/server/sonar-web/src/main/js/api/users.ts b/server/sonar-web/src/main/js/api/users.ts index d75c811ff95..68c37e2d869 100644 --- a/server/sonar-web/src/main/js/api/users.ts +++ b/server/sonar-web/src/main/js/api/users.ts @@ -75,20 +75,6 @@ export function getIdentityProviders(): Promise<{ identityProviders: IdentityPro return getJSON('/api/users/identity_providers').catch(throwGlobalError); } -export function searchUsers(data: { - p?: number; - ps?: number; - q?: string; - managed?: boolean; - lastConnectedAfter?: string; - lastConnectedBefore?: string; - slLastConnectedAfter?: string; - slLastConnectedBefore?: string; -}): Promise<{ paging: Paging; users: User[] }> { - data.q = data.q || undefined; - return getJSON('/api/users/search', data).catch(throwGlobalError); -} - export function getUsers(data: { q: string; active?: boolean; diff --git a/server/sonar-web/src/main/js/apps/issues/components/AssigneeSelect.tsx b/server/sonar-web/src/main/js/apps/issues/components/AssigneeSelect.tsx index 5bca99c6339..664518cad3d 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/AssigneeSelect.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/AssigneeSelect.tsx @@ -24,7 +24,7 @@ import { CurrentUserContext } from '../../../app/components/current-user/Current import Avatar from '../../../components/ui/Avatar'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Issue } from '../../../types/types'; -import { UserActive, isLoggedIn, isUserActive } from '../../../types/users'; +import { RestUser, isLoggedIn, isUserActive } from '../../../types/users'; import { searchAssignees } from '../utils'; // exported for test @@ -40,7 +40,7 @@ export interface AssigneeSelectProps { inputId: string; } -function userToOption(user: UserActive) { +function userToOption(user: RestUser) { const userInfo = user.name || user.login; return { value: user.login, @@ -58,7 +58,7 @@ export default function AssigneeSelect(props: AssigneeSelectProps) { isLoggedIn(currentUser) && issues.some((issue) => currentUser.login !== issue.assignee); const defaultOptions = allowCurrentUserSelection - ? [UNASSIGNED, userToOption(currentUser)] + ? [UNASSIGNED, userToOption(currentUser as unknown as RestUser)] : [UNASSIGNED]; const controlLabel = assignee ? ( diff --git a/server/sonar-web/src/main/js/apps/issues/utils.ts b/server/sonar-web/src/main/js/apps/issues/utils.ts index 9638728d3ab..dd499df95d5 100644 --- a/server/sonar-web/src/main/js/apps/issues/utils.ts +++ b/server/sonar-web/src/main/js/apps/issues/utils.ts @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { isArray } from 'lodash'; -import { searchUsers } from '../../api/users'; +import { getUsers } from '../../api/users'; import { formatMeasure } from '../../helpers/measures'; import { cleanQuery, @@ -37,7 +37,7 @@ import { Facet, RawFacet } from '../../types/issues'; import { MetricType } from '../../types/metrics'; import { SecurityStandard } from '../../types/security'; import { Dict, Issue, Paging, RawQuery } from '../../types/types'; -import { UserBase } from '../../types/users'; +import { RestUser } from '../../types/users'; const OWASP_ASVS_4_0 = 'owaspAsvs-4.0'; @@ -192,9 +192,9 @@ export function formatFacetStat(stat: number | undefined) { export const searchAssignees = ( query: string, page = 1 -): Promise<{ paging: Paging; results: UserBase[] }> => { - return searchUsers({ p: page, q: query }).then(({ paging, users }) => ({ - paging, +): Promise<{ paging: Paging; results: RestUser[] }> => { + return getUsers({ pageIndex: page, q: query }).then(({ pageRestResponse, users }) => ({ + paging: pageRestResponse, results: users, })); }; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx index 07d5ffe584b..68ad9c44b9c 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-it.tsx @@ -26,7 +26,7 @@ import BranchesServiceMock from '../../../api/mocks/BranchesServiceMock'; import CodingRulesServiceMock from '../../../api/mocks/CodingRulesServiceMock'; import SecurityHotspotServiceMock from '../../../api/mocks/SecurityHotspotServiceMock'; import { getSecurityHotspots, setSecurityHotspotStatus } from '../../../api/security-hotspots'; -import { searchUsers } from '../../../api/users'; +import { getUsers } from '../../../api/users'; import { mockComponent } from '../../../helpers/mocks/component'; import { openHotspot, probeSonarLintServers } from '../../../helpers/sonarlint'; import { get, save } from '../../../helpers/storage'; @@ -224,7 +224,7 @@ describe('CRUD', () => { await user.keyboard('User'); }); - expect(searchUsers).toHaveBeenLastCalledWith({ q: 'User' }); + expect(getUsers).toHaveBeenLastCalledWith({ q: 'User' }); await user.keyboard('{Enter}'); expect(ui.successGlobalMessage.get()).toHaveTextContent(`hotspots.assign.success.User John`); }); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/Assignee.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/Assignee.tsx index 8631310d85d..c732c7b7e2d 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/Assignee.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/Assignee.tsx @@ -22,13 +22,13 @@ import { noop } from 'lodash'; import * as React from 'react'; import { Options, SingleValue } from 'react-select'; import { assignSecurityHotspot } from '../../../api/security-hotspots'; -import { searchUsers } from '../../../api/users'; +import { getUsers } from '../../../api/users'; import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext'; import Avatar from '../../../components/ui/Avatar'; import { addGlobalSuccessMessage } from '../../../helpers/globalMessages'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Hotspot, HotspotResolution, HotspotStatus } from '../../../types/security-hotspots'; -import { isLoggedIn, isUserActive } from '../../../types/users'; +import { RestUser, isLoggedIn, isUserActive } from '../../../types/users'; interface Props { hotspot: Hotspot; @@ -78,7 +78,7 @@ export default function Assignee(props: Props) { query: string, cb: (options: Options>) => void ) => { - searchUsers({ q: query }) + getUsers({ q: query }) .then((result) => { const options: Array> = result.users .filter(isUserActive) diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx index 4ca851e5a39..fa0e959ff83 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx @@ -20,11 +20,11 @@ import { LabelValueSelectOption, PopupZLevel, SearchSelectDropdown } from 'design-system'; import * as React from 'react'; import { Options, SingleValue } from 'react-select'; -import { searchUsers } from '../../../api/users'; +import { getUsers } from '../../../api/users'; import { CurrentUserContext } from '../../../app/components/current-user/CurrentUserContext'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { Issue } from '../../../types/types'; -import { isLoggedIn, isUserActive } from '../../../types/users'; +import { RestUser, isLoggedIn, isUserActive } from '../../../types/users'; import Avatar from '../../ui/Avatar'; interface Props { @@ -85,7 +85,7 @@ export default function IssueAssignee(props: Props) { query: string, cb: (options: Options>) => void ) => { - searchUsers({ q: query }) + getUsers({ q: query }) .then((result) => { const options: Array> = result.users .filter(isUserActive) -- 2.39.5