From: Sarath Nair <91882341+sarath-nair-sonarsource@users.noreply.github.com> Date: Thu, 29 Aug 2024 07:28:09 +0000 (+0200) Subject: SONAR-22862 Refactor permission mapping API changes (#11630) X-Git-Tag: 10.7.0.96327~147 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=eaa0d3a6b3e864e8f0624496a73a59b2ac85960e;p=sonarqube.git SONAR-22862 Refactor permission mapping API changes (#11630) --- diff --git a/server/sonar-web/src/main/js/api/github-provisioning.ts b/server/sonar-web/src/main/js/api/github-provisioning.ts index eb9709da510..1cb688d5755 100644 --- a/server/sonar-web/src/main/js/api/github-provisioning.ts +++ b/server/sonar-web/src/main/js/api/github-provisioning.ts @@ -21,7 +21,7 @@ import axios from 'axios'; import { throwGlobalError } from '~sonar-aligned/helpers/error'; import { getJSON } from '~sonar-aligned/helpers/request'; import { post, postJSON } from '../helpers/request'; -import { GitHubConfigurationStatus, GitHubMapping, GithubStatus } from '../types/provisioning'; +import { DevopsRolesMapping, GitHubConfigurationStatus, GithubStatus } from '../types/provisioning'; const GITHUB_PERMISSION_MAPPINGS = '/api/v2/dop-translation/github-permission-mappings'; @@ -39,22 +39,22 @@ export function syncNowGithubProvisioning(): Promise { export function fetchGithubRolesMapping() { return axios - .get<{ githubPermissionsMappings: GitHubMapping[] }>(GITHUB_PERMISSION_MAPPINGS) - .then((data) => data.githubPermissionsMappings); + .get<{ permissionMappings: DevopsRolesMapping[] }>(GITHUB_PERMISSION_MAPPINGS) + .then((data) => data.permissionMappings); } export function updateGithubRolesMapping( role: string, - data: Partial>, + data: Partial>, ) { - return axios.patch( + return axios.patch( `${GITHUB_PERMISSION_MAPPINGS}/${encodeURIComponent(role)}`, data, ); } -export function addGithubRolesMapping(data: Omit) { - return axios.post(GITHUB_PERMISSION_MAPPINGS, data); +export function addGithubRolesMapping(data: Omit) { + return axios.post(GITHUB_PERMISSION_MAPPINGS, data); } export function deleteGithubRolesMapping(role: string) { diff --git a/server/sonar-web/src/main/js/api/gitlab-provisioning.ts b/server/sonar-web/src/main/js/api/gitlab-provisioning.ts index 59f10624eb0..e69a372d799 100644 --- a/server/sonar-web/src/main/js/api/gitlab-provisioning.ts +++ b/server/sonar-web/src/main/js/api/gitlab-provisioning.ts @@ -19,9 +19,9 @@ */ import axios from 'axios'; import { + DevopsRolesMapping, GitLabConfigurationCreateBody, GitLabConfigurationUpdateBody, - GitLabMapping, GitlabConfiguration, ProvisioningType, } from '../types/provisioning'; @@ -69,22 +69,22 @@ export function syncNowGitLabProvisioning(): Promise { export function fetchGitlabRolesMapping() { return axios - .get<{ gitlabPermissionsMappings: GitLabMapping[] }>(GITLAB_PERMISSION_MAPPINGS) - .then((data) => data.gitlabPermissionsMappings); + .get<{ permissionMappings: DevopsRolesMapping[] }>(GITLAB_PERMISSION_MAPPINGS) + .then((data) => data.permissionMappings); } export function updateGitlabRolesMapping( role: string, - data: Partial>, + data: Partial>, ) { - return axios.patch( + return axios.patch( `${GITLAB_PERMISSION_MAPPINGS}/${encodeURIComponent(role)}`, data, ); } -export function addGitlabRolesMapping(data: Omit) { - return axios.post(GITLAB_PERMISSION_MAPPINGS, data); +export function addGitlabRolesMapping(data: Omit) { + return axios.post(GITLAB_PERMISSION_MAPPINGS, data); } export function deleteGitlabRolesMapping(role: string) { diff --git a/server/sonar-web/src/main/js/api/mocks/GithubProvisioningServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/GithubProvisioningServiceMock.ts index cdb223417d6..aa0c6b78d03 100644 --- a/server/sonar-web/src/main/js/api/mocks/GithubProvisioningServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/GithubProvisioningServiceMock.ts @@ -20,8 +20,8 @@ import { cloneDeep } from 'lodash'; import { mockTask } from '../../helpers/mocks/tasks'; import { + DevopsRolesMapping, GitHubConfigurationStatus, - GitHubMapping, GitHubProvisioningStatus, ProvisioningType, } from '../../types/provisioning'; @@ -62,11 +62,11 @@ const defaultConfigurationStatus: GitHubConfigurationStatus = { const githubMappingMock = ( id: string, - permissions: (keyof GitHubMapping['permissions'])[], + permissions: (keyof DevopsRolesMapping['permissions'])[], baseRole = false, ) => ({ id, - githubRole: id, + role: id, baseRole, permissions: { user: permissions.includes('user'), @@ -78,7 +78,7 @@ const githubMappingMock = ( }, }); -const defaultMapping: GitHubMapping[] = [ +const defaultMapping: DevopsRolesMapping[] = [ githubMappingMock('read', ['user', 'codeViewer'], true), githubMappingMock( 'write', @@ -101,7 +101,7 @@ const defaultMapping: GitHubMapping[] = [ export default class GithubProvisioningServiceMock { dopTranslationServiceMock?: DopTranslationServiceMock; githubConfigurationStatus: GitHubConfigurationStatus; - githubMapping: GitHubMapping[]; + githubMapping: DevopsRolesMapping[]; tasks: Task[]; constructor(dopTranslationServiceMock?: DopTranslationServiceMock) { @@ -185,12 +185,12 @@ export default class GithubProvisioningServiceMock { ); return Promise.resolve( - this.githubMapping.find((mapping) => mapping.id === id) as GitHubMapping, + this.githubMapping.find((mapping) => mapping.id === id) as DevopsRolesMapping, ); }; handleAddGithubRolesMapping: typeof addGithubRolesMapping = (data) => { - const newRole = { ...data, id: data.githubRole }; + const newRole = { ...data, id: data.role }; this.githubMapping = [...this.githubMapping, newRole]; return Promise.resolve(newRole); @@ -201,7 +201,7 @@ export default class GithubProvisioningServiceMock { return Promise.resolve(); }; - addGitHubCustomRole = (id: string, permissions: (keyof GitHubMapping['permissions'])[]) => { + addGitHubCustomRole = (id: string, permissions: (keyof DevopsRolesMapping['permissions'])[]) => { this.githubMapping = [...this.githubMapping, githubMappingMock(id, permissions)]; }; diff --git a/server/sonar-web/src/main/js/api/mocks/GitlabProvisioningServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/GitlabProvisioningServiceMock.ts index e7b9529143b..ab66b53b0f7 100644 --- a/server/sonar-web/src/main/js/api/mocks/GitlabProvisioningServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/GitlabProvisioningServiceMock.ts @@ -20,7 +20,7 @@ import { cloneDeep, omit } from 'lodash'; import { mockGitlabConfiguration } from '../../helpers/mocks/alm-integrations'; import { mockPaging } from '../../helpers/testMocks'; -import { GitLabMapping, GitlabConfiguration } from '../../types/provisioning'; +import { DevopsRolesMapping, GitlabConfiguration } from '../../types/provisioning'; import { addGitlabRolesMapping, createGitLabConfiguration, @@ -41,11 +41,11 @@ const defaultGitlabConfiguration: GitlabConfiguration[] = [ const gitlabMappingMock = ( id: string, - permissions: (keyof GitLabMapping['permissions'])[], + permissions: (keyof DevopsRolesMapping['permissions'])[], baseRole = false, ) => ({ id, - gitlabRole: id, + role: id, baseRole, permissions: { user: permissions.includes('user'), @@ -57,7 +57,7 @@ const gitlabMappingMock = ( }, }); -const defaultMapping: GitLabMapping[] = [ +const defaultMapping: DevopsRolesMapping[] = [ gitlabMappingMock('guest', ['user', 'codeViewer'], true), gitlabMappingMock('reporter', ['user', 'codeViewer'], true), gitlabMappingMock( @@ -79,7 +79,7 @@ const defaultMapping: GitLabMapping[] = [ export default class GitlabProvisioningServiceMock { gitlabConfigurations: GitlabConfiguration[]; - gitlabMapping: GitLabMapping[]; + gitlabMapping: DevopsRolesMapping[]; constructor() { this.gitlabConfigurations = cloneDeep(defaultGitlabConfiguration); @@ -143,12 +143,12 @@ export default class GitlabProvisioningServiceMock { ); return Promise.resolve( - this.gitlabMapping.find((mapping) => mapping.id === id) as GitLabMapping, + this.gitlabMapping.find((mapping) => mapping.id === id) as DevopsRolesMapping, ); }; handleAddGitlabRolesMapping: typeof addGitlabRolesMapping = (data) => { - const newRole = { ...data, id: data.gitlabRole }; + const newRole = { ...data, id: data.role }; this.gitlabMapping = [...this.gitlabMapping, newRole]; return Promise.resolve(newRole); @@ -159,7 +159,7 @@ export default class GitlabProvisioningServiceMock { return Promise.resolve(); }; - addGitLabCustomRole = (id: string, permissions: (keyof GitLabMapping['permissions'])[]) => { + addGitLabCustomRole = (id: string, permissions: (keyof DevopsRolesMapping['permissions'])[]) => { this.gitlabMapping = [...this.gitlabMapping, gitlabMappingMock(id, permissions)]; }; diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/DevopsRolesMappingModal.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/DevopsRolesMappingModal.tsx index 4fe480028e4..4b70d1140d5 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/DevopsRolesMappingModal.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/DevopsRolesMappingModal.tsx @@ -42,25 +42,23 @@ import { isPermissionDefinitionGroup, } from '../../../../helpers/permissions'; import { AlmKeys } from '../../../../types/alm-settings'; -import { GitHubMapping, GitLabMapping } from '../../../../types/provisioning'; - -type RolesMapping = GitHubMapping[] | GitLabMapping[] | null; +import { DevopsRolesMapping } from '../../../../types/provisioning'; interface Props { isLoading: boolean; - mapping: RolesMapping; + mapping: DevopsRolesMapping[] | null; mappingFor: AlmKeys.GitHub | AlmKeys.GitLab; onClose: () => void; - roles?: RolesMapping; - setMapping: React.Dispatch>; + roles?: DevopsRolesMapping[] | null; + setMapping: React.Dispatch>; } interface PermissionCellProps extends Pick { - list?: GitHubMapping[] | GitLabMapping[]; - mapping: GitHubMapping | GitLabMapping; + list?: DevopsRolesMapping[]; + mapping: DevopsRolesMapping; } -const DEFAULT_CUSTOM_ROLE_PERMISSIONS: GitHubMapping['permissions'] = { +const DEFAULT_CUSTOM_ROLE_PERMISSIONS: DevopsRolesMapping['permissions'] = { user: true, codeViewer: false, issueAdmin: false, @@ -71,30 +69,21 @@ const DEFAULT_CUSTOM_ROLE_PERMISSIONS: GitHubMapping['permissions'] = { function PermissionRow(props: Readonly) { const { list, mapping } = props; - const isGitHubMapping = 'githubRole' in mapping; - const role = isGitHubMapping ? mapping.githubRole : mapping.gitlabRole; - const isBaseRole = mapping.baseRole; + const { role, baseRole } = mapping; const setMapping = () => { - if (isGitHubMapping) { - return props.setMapping( - (list as GitHubMapping[])?.filter((r) => r.githubRole !== role) ?? null, - ); - } - return props.setMapping( - (list as GitLabMapping[])?.filter((r) => r.gitlabRole !== role) ?? null, - ); + return props.setMapping(list?.filter((r) => r.role !== role) ?? null); }; return (
- + {role} - {!isBaseRole && ( + {!baseRole && ( ) { checked={value} onCheck={(newValue) => props.setMapping( - (list?.map((item) => + list?.map((item) => item.id === mapping.id ? { ...item, permissions: { ...item.permissions, [key]: newValue } } : item, - ) ?? null) as RolesMapping, + ) ?? null, ) } /> @@ -148,33 +137,17 @@ export function DevopsRolesMappingModal(props: Readonly) { e.preventDefault(); const value = customRoleInput.trim(); if ( - mappingFor === AlmKeys.GitHub && - !(list as GitHubMapping[])?.some((el) => - el.baseRole ? el.githubRole.toLowerCase() === value.toLowerCase() : el.githubRole === value, - ) - ) { - setMapping([ - { - id: customRoleInput, - githubRole: customRoleInput, - permissions: { ...DEFAULT_CUSTOM_ROLE_PERMISSIONS }, - }, - ...((list as GitHubMapping[]) ?? []), - ]); - setCustomRoleInput(''); - } else if ( - mappingFor === AlmKeys.GitLab && - !(list as GitLabMapping[])?.some((el) => - el.baseRole ? el.gitlabRole.toLowerCase() === value.toLowerCase() : el.gitlabRole === value, + !list?.some((el) => + el.baseRole ? el.role.toLowerCase() === value.toLowerCase() : el.role === value, ) ) { setMapping([ { id: customRoleInput, - gitlabRole: customRoleInput, + role: customRoleInput, permissions: { ...DEFAULT_CUSTOM_ROLE_PERMISSIONS }, }, - ...((list as GitLabMapping[]) ?? []), + ...(list ?? []), ]); setCustomRoleInput(''); } else { diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubAuthenticationTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubAuthenticationTab.tsx index 37ec3ec70e0..057bebd138b 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubAuthenticationTab.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubAuthenticationTab.tsx @@ -40,7 +40,7 @@ import { } from '../../../../queries/identity-provider/github'; import { GitHubConfigurationPayload } from '../../../../types/dop-translation'; import { Feature } from '../../../../types/features'; -import { GitHubMapping, ProvisioningType } from '../../../../types/provisioning'; +import { DevopsRolesMapping, ProvisioningType } from '../../../../types/provisioning'; import { Provider } from '../../../../types/types'; import AuthenticationFormField from './AuthenticationFormField'; import AutoProvisioningConsent from './AutoProvisionningConsent'; @@ -63,7 +63,7 @@ export default function GitHubAuthenticationTab() { const [tokenKey, setTokenKey] = React.useState(0); const [isConfirmProvisioningModalOpen, setIsConfirmProvisioningModalOpen] = React.useState(false); const [isMappingModalOpen, setIsMappingModalOpen] = useState(false); - const [rolesMapping, setRolesMapping] = useState(null); + const [rolesMapping, setRolesMapping] = useState(null); const hasGithubProvisioning = useContext(AvailableFeaturesContext).includes( Feature.GithubProvisioning, diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubMappingModal.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubMappingModal.tsx index 8b4a26362c9..cfde5ce3396 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubMappingModal.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitHubMappingModal.tsx @@ -20,13 +20,13 @@ import * as React from 'react'; import { useGithubRolesMappingQuery } from '../../../../queries/identity-provider/github'; import { AlmKeys } from '../../../../types/alm-settings'; -import { GitHubMapping } from '../../../../types/provisioning'; +import { DevopsRolesMapping } from '../../../../types/provisioning'; import { DevopsRolesMappingModal } from './DevopsRolesMappingModal'; interface Props { - mapping: GitHubMapping[] | null; + mapping: DevopsRolesMapping[] | null; onClose: () => void; - setMapping: React.Dispatch>; + setMapping: React.Dispatch>; } export default function GitHubMappingModal(props: Readonly) { diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabAuthenticationTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabAuthenticationTab.tsx index 67dcab52af5..55e696a0a6f 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabAuthenticationTab.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabAuthenticationTab.tsx @@ -37,8 +37,8 @@ import { } from '../../../../queries/identity-provider/gitlab'; import { Feature } from '../../../../types/features'; import { + DevopsRolesMapping, GitLabConfigurationUpdateBody, - GitLabMapping, ProvisioningType, } from '../../../../types/provisioning'; import { DefinitionV2, SettingType } from '../../../../types/settings'; @@ -95,7 +95,7 @@ export default function GitLabAuthenticationTab() { const [tokenKey, setTokenKey] = useState(0); const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = useState(false); const [isMappingModalOpen, setIsMappingModalOpen] = useState(false); - const [rolesMapping, setRolesMapping] = useState(null); + const [rolesMapping, setRolesMapping] = useState(null); const { mutateAsync: updateMapping } = useGitlabRolesMappingMutation(); const hasGitlabProvisioningFeature = useContext(AvailableFeaturesContext).includes( diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabMappingModal.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabMappingModal.tsx index b5b8c2a60f4..e74bc66920a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabMappingModal.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/GitLabMappingModal.tsx @@ -20,13 +20,13 @@ import * as React from 'react'; import { useGitlabRolesMappingQuery } from '../../../../queries/identity-provider/gitlab'; import { AlmKeys } from '../../../../types/alm-settings'; -import { GitLabMapping } from '../../../../types/provisioning'; +import { DevopsRolesMapping } from '../../../../types/provisioning'; import { DevopsRolesMappingModal } from './DevopsRolesMappingModal'; interface Props { - mapping: GitLabMapping[] | null; + mapping: DevopsRolesMapping[] | null; onClose: () => void; - setMapping: React.Dispatch>; + setMapping: React.Dispatch>; } export default function GitLabMappingModal(props: Readonly) { diff --git a/server/sonar-web/src/main/js/queries/identity-provider/github.ts b/server/sonar-web/src/main/js/queries/identity-provider/github.ts index 41407db3f7a..04c03109f11 100644 --- a/server/sonar-web/src/main/js/queries/identity-provider/github.ts +++ b/server/sonar-web/src/main/js/queries/identity-provider/github.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { addGlobalSuccessMessage } from 'design-system'; import { isEqual, keyBy, partition, pick, unionBy } from 'lodash'; import { useContext } from 'react'; @@ -34,9 +34,8 @@ import { AvailableFeaturesContext } from '../../app/components/available-feature import { translate } from '../../helpers/l10n'; import { mapReactQueryResult } from '../../helpers/react-query'; import { Feature } from '../../types/features'; -import { GitHubMapping } from '../../types/provisioning'; - -const MAPPING_STALE_TIME = 60_000; +import { DevopsRolesMapping } from '../../types/provisioning'; +import { StaleTime, createQueryHook } from '../common'; export const useCheckGitHubConfigQuery = (githubEnabled: boolean) => { return useQuery({ @@ -89,27 +88,30 @@ export function useSyncWithGitHubNow() { // Order is reversed to put custom roles at the end (their index is -1) const defaultRoleOrder = ['admin', 'maintain', 'write', 'triage', 'read']; -export function useGithubRolesMappingQuery() { - return useQuery({ +function sortGithubRoles(data: DevopsRolesMapping[]) { + return [...data].sort((a, b) => { + if (defaultRoleOrder.includes(a.id) || defaultRoleOrder.includes(b.id)) { + return defaultRoleOrder.indexOf(b.id) - defaultRoleOrder.indexOf(a.id); + } + return a.role.localeCompare(b.role); + }); +} + +export const useGithubRolesMappingQuery = createQueryHook(() => { + return queryOptions({ queryKey: ['identity_provider', 'github_mapping'], queryFn: fetchGithubRolesMapping, - staleTime: MAPPING_STALE_TIME, - select: (data) => - [...data].sort((a, b) => { - if (defaultRoleOrder.includes(a.id) || defaultRoleOrder.includes(b.id)) { - return defaultRoleOrder.indexOf(b.id) - defaultRoleOrder.indexOf(a.id); - } - return a.githubRole.localeCompare(b.githubRole); - }), + staleTime: StaleTime.LONG, + select: sortGithubRoles, // uses a stable function reference }); -} +}); export function useGithubRolesMappingMutation() { const client = useQueryClient(); const queryKey = ['identity_provider', 'github_mapping']; return useMutation({ - mutationFn: async (mapping: GitHubMapping[]) => { - const state = keyBy(client.getQueryData(queryKey), (m) => m.id); + mutationFn: async (mapping: DevopsRolesMapping[]) => { + const state = keyBy(client.getQueryData(queryKey), (m) => m.id); const [maybeChangedRoles, newRoles] = partition(mapping, (m) => state[m.id]); const changedRoles = maybeChangedRoles.filter((item) => !isEqual(item, state[item.id])); @@ -130,7 +132,7 @@ export function useGithubRolesMappingMutation() { }; }, onSuccess: ({ addedOrChanged, deleted }) => { - const state = client.getQueryData(queryKey); + const state = client.getQueryData(queryKey); if (state) { const newData = unionBy( addedOrChanged, diff --git a/server/sonar-web/src/main/js/queries/identity-provider/gitlab.ts b/server/sonar-web/src/main/js/queries/identity-provider/gitlab.ts index fb1bb25436a..a88cb9612b3 100644 --- a/server/sonar-web/src/main/js/queries/identity-provider/gitlab.ts +++ b/server/sonar-web/src/main/js/queries/identity-provider/gitlab.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { queryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { addGlobalSuccessMessage } from 'design-system'; import { isEqual, keyBy, partition, pick, unionBy } from 'lodash'; import { getActivity } from '../../api/ce'; @@ -34,10 +34,9 @@ import { } from '../../api/gitlab-provisioning'; import { translate } from '../../helpers/l10n'; import { mapReactQueryResult } from '../../helpers/react-query'; -import { AlmSyncStatus, GitLabMapping, ProvisioningType } from '../../types/provisioning'; +import { AlmSyncStatus, DevopsRolesMapping, ProvisioningType } from '../../types/provisioning'; import { TaskStatuses, TaskTypes } from '../../types/tasks'; - -const MAPPING_STALE_TIME = 60_000; +import { createQueryHook, StaleTime } from '../common'; export function useGitLabConfigurationsQuery() { return useQuery({ @@ -200,27 +199,30 @@ export function useSyncWithGitLabNow() { // Order is reversed to put custom roles at the end (their index is -1) const defaultRoleOrder = ['owner', 'maintainer', 'developer', 'reporter', 'guest']; -export function useGitlabRolesMappingQuery() { - return useQuery({ +function sortGitlabRoles(data: DevopsRolesMapping[]) { + return [...data].sort((a, b) => { + if (defaultRoleOrder.includes(a.id) || defaultRoleOrder.includes(b.id)) { + return defaultRoleOrder.indexOf(b.id) - defaultRoleOrder.indexOf(a.id); + } + return a.role.localeCompare(b.role); + }); +} + +export const useGitlabRolesMappingQuery = createQueryHook(() => { + return queryOptions({ queryKey: ['identity_provider', 'gitlab_mapping'], queryFn: fetchGitlabRolesMapping, - staleTime: MAPPING_STALE_TIME, - select: (data) => - [...data].sort((a, b) => { - if (defaultRoleOrder.includes(a.id) || defaultRoleOrder.includes(b.id)) { - return defaultRoleOrder.indexOf(b.id) - defaultRoleOrder.indexOf(a.id); - } - return a.gitlabRole.localeCompare(b.gitlabRole); - }), + staleTime: StaleTime.LONG, + select: sortGitlabRoles, }); -} +}); export function useGitlabRolesMappingMutation() { const client = useQueryClient(); const queryKey = ['identity_provider', 'gitlab_mapping']; return useMutation({ - mutationFn: async (mapping: GitLabMapping[]) => { - const state = keyBy(client.getQueryData(queryKey), (m) => m.id); + mutationFn: async (mapping: DevopsRolesMapping[]) => { + const state = keyBy(client.getQueryData(queryKey), (m) => m.id); const [maybeChangedRoles, newRoles] = partition(mapping, (m) => state[m.id]); const changedRoles = maybeChangedRoles.filter((item) => !isEqual(item, state[item.id])); @@ -241,7 +243,7 @@ export function useGitlabRolesMappingMutation() { }; }, onSuccess: ({ addedOrChanged, deleted }) => { - const state = client.getQueryData(queryKey); + const state = client.getQueryData(queryKey); if (state) { const newData = unionBy( addedOrChanged, diff --git a/server/sonar-web/src/main/js/types/provisioning.ts b/server/sonar-web/src/main/js/types/provisioning.ts index ac47a51e196..c15fcd5afa0 100644 --- a/server/sonar-web/src/main/js/types/provisioning.ts +++ b/server/sonar-web/src/main/js/types/provisioning.ts @@ -78,7 +78,7 @@ export interface GitHubConfigurationStatus { }[]; } -interface DevopsRolesMapping { +export interface DevopsRolesMapping { readonly baseRole?: boolean; readonly id: string; permissions: { @@ -89,14 +89,7 @@ interface DevopsRolesMapping { securityHotspotAdmin: boolean; user: boolean; }; -} - -export interface GitHubMapping extends DevopsRolesMapping { - readonly githubRole: string; -} - -export interface GitLabMapping extends DevopsRolesMapping { - readonly gitlabRole: string; + readonly role: string; } export interface GitLabConfigurationCreateBody