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';
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<Pick<GitHubMapping, 'permissions'>>,
+ data: Partial<Pick<DevopsRolesMapping, 'permissions'>>,
) {
- return axios.patch<GitHubMapping>(
+ return axios.patch<DevopsRolesMapping>(
`${GITHUB_PERMISSION_MAPPINGS}/${encodeURIComponent(role)}`,
data,
);
}
-export function addGithubRolesMapping(data: Omit<GitHubMapping, 'id'>) {
- return axios.post<GitHubMapping>(GITHUB_PERMISSION_MAPPINGS, data);
+export function addGithubRolesMapping(data: Omit<DevopsRolesMapping, 'id'>) {
+ return axios.post<DevopsRolesMapping>(GITHUB_PERMISSION_MAPPINGS, data);
}
export function deleteGithubRolesMapping(role: string) {
*/
import axios from 'axios';
import {
+ DevopsRolesMapping,
GitLabConfigurationCreateBody,
GitLabConfigurationUpdateBody,
- GitLabMapping,
GitlabConfiguration,
ProvisioningType,
} from '../types/provisioning';
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<Pick<GitLabMapping, 'permissions'>>,
+ data: Partial<Pick<DevopsRolesMapping, 'permissions'>>,
) {
- return axios.patch<GitLabMapping>(
+ return axios.patch<DevopsRolesMapping>(
`${GITLAB_PERMISSION_MAPPINGS}/${encodeURIComponent(role)}`,
data,
);
}
-export function addGitlabRolesMapping(data: Omit<GitLabMapping, 'id'>) {
- return axios.post<GitLabMapping>(GITLAB_PERMISSION_MAPPINGS, data);
+export function addGitlabRolesMapping(data: Omit<DevopsRolesMapping, 'id'>) {
+ return axios.post<DevopsRolesMapping>(GITLAB_PERMISSION_MAPPINGS, data);
}
export function deleteGitlabRolesMapping(role: string) {
import { cloneDeep } from 'lodash';
import { mockTask } from '../../helpers/mocks/tasks';
import {
+ DevopsRolesMapping,
GitHubConfigurationStatus,
- GitHubMapping,
GitHubProvisioningStatus,
ProvisioningType,
} from '../../types/provisioning';
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'),
},
});
-const defaultMapping: GitHubMapping[] = [
+const defaultMapping: DevopsRolesMapping[] = [
githubMappingMock('read', ['user', 'codeViewer'], true),
githubMappingMock(
'write',
export default class GithubProvisioningServiceMock {
dopTranslationServiceMock?: DopTranslationServiceMock;
githubConfigurationStatus: GitHubConfigurationStatus;
- githubMapping: GitHubMapping[];
+ githubMapping: DevopsRolesMapping[];
tasks: Task[];
constructor(dopTranslationServiceMock?: DopTranslationServiceMock) {
);
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);
return Promise.resolve();
};
- addGitHubCustomRole = (id: string, permissions: (keyof GitHubMapping['permissions'])[]) => {
+ addGitHubCustomRole = (id: string, permissions: (keyof DevopsRolesMapping['permissions'])[]) => {
this.githubMapping = [...this.githubMapping, githubMappingMock(id, permissions)];
};
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,
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'),
},
});
-const defaultMapping: GitLabMapping[] = [
+const defaultMapping: DevopsRolesMapping[] = [
gitlabMappingMock('guest', ['user', 'codeViewer'], true),
gitlabMappingMock('reporter', ['user', 'codeViewer'], true),
gitlabMappingMock(
export default class GitlabProvisioningServiceMock {
gitlabConfigurations: GitlabConfiguration[];
- gitlabMapping: GitLabMapping[];
+ gitlabMapping: DevopsRolesMapping[];
constructor() {
this.gitlabConfigurations = cloneDeep(defaultGitlabConfiguration);
);
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);
return Promise.resolve();
};
- addGitLabCustomRole = (id: string, permissions: (keyof GitLabMapping['permissions'])[]) => {
+ addGitLabCustomRole = (id: string, permissions: (keyof DevopsRolesMapping['permissions'])[]) => {
this.gitlabMapping = [...this.gitlabMapping, gitlabMappingMock(id, permissions)];
};
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<React.SetStateAction<RolesMapping>>;
+ roles?: DevopsRolesMapping[] | null;
+ setMapping: React.Dispatch<React.SetStateAction<DevopsRolesMapping[] | null>>;
}
interface PermissionCellProps extends Pick<Props, 'setMapping'> {
- 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,
function PermissionRow(props: Readonly<PermissionCellProps>) {
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 (
<TableRowInteractive>
<ContentCell scope="row" className="sw-whitespace-nowrap">
<div className="sw-flex sw-max-w-[330px] sw-items-center">
- <b className={isBaseRole ? 'sw-capitalize' : 'sw-truncate'} title={role}>
+ <b className={baseRole ? 'sw-capitalize' : 'sw-truncate'} title={role}>
{role}
</b>
- {!isBaseRole && (
+ {!baseRole && (
<DestructiveIcon
className="sw-ml-1"
aria-label={translateWithParameters(
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,
)
}
/>
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 {
} 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';
const [tokenKey, setTokenKey] = React.useState<number>(0);
const [isConfirmProvisioningModalOpen, setIsConfirmProvisioningModalOpen] = React.useState(false);
const [isMappingModalOpen, setIsMappingModalOpen] = useState(false);
- const [rolesMapping, setRolesMapping] = useState<GitHubMapping[] | null>(null);
+ const [rolesMapping, setRolesMapping] = useState<DevopsRolesMapping[] | null>(null);
const hasGithubProvisioning = useContext(AvailableFeaturesContext).includes(
Feature.GithubProvisioning,
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<React.SetStateAction<GitHubMapping[] | null>>;
+ setMapping: React.Dispatch<React.SetStateAction<DevopsRolesMapping[] | null>>;
}
export default function GitHubMappingModal(props: Readonly<Props>) {
} from '../../../../queries/identity-provider/gitlab';
import { Feature } from '../../../../types/features';
import {
+ DevopsRolesMapping,
GitLabConfigurationUpdateBody,
- GitLabMapping,
ProvisioningType,
} from '../../../../types/provisioning';
import { DefinitionV2, SettingType } from '../../../../types/settings';
const [tokenKey, setTokenKey] = useState<number>(0);
const [showConfirmProvisioningModal, setShowConfirmProvisioningModal] = useState(false);
const [isMappingModalOpen, setIsMappingModalOpen] = useState(false);
- const [rolesMapping, setRolesMapping] = useState<GitLabMapping[] | null>(null);
+ const [rolesMapping, setRolesMapping] = useState<DevopsRolesMapping[] | null>(null);
const { mutateAsync: updateMapping } = useGitlabRolesMappingMutation();
const hasGitlabProvisioningFeature = useContext(AvailableFeaturesContext).includes(
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<React.SetStateAction<GitLabMapping[] | null>>;
+ setMapping: React.Dispatch<React.SetStateAction<DevopsRolesMapping[] | null>>;
}
export default function GitLabMappingModal(props: Readonly<Props>) {
* 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';
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({
// 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<GitHubMapping[]>(queryKey), (m) => m.id);
+ mutationFn: async (mapping: DevopsRolesMapping[]) => {
+ const state = keyBy(client.getQueryData<DevopsRolesMapping[]>(queryKey), (m) => m.id);
const [maybeChangedRoles, newRoles] = partition(mapping, (m) => state[m.id]);
const changedRoles = maybeChangedRoles.filter((item) => !isEqual(item, state[item.id]));
};
},
onSuccess: ({ addedOrChanged, deleted }) => {
- const state = client.getQueryData<GitHubMapping[]>(queryKey);
+ const state = client.getQueryData<DevopsRolesMapping[]>(queryKey);
if (state) {
const newData = unionBy(
addedOrChanged,
* 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';
} 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({
// 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<GitLabMapping[]>(queryKey), (m) => m.id);
+ mutationFn: async (mapping: DevopsRolesMapping[]) => {
+ const state = keyBy(client.getQueryData<DevopsRolesMapping[]>(queryKey), (m) => m.id);
const [maybeChangedRoles, newRoles] = partition(mapping, (m) => state[m.id]);
const changedRoles = maybeChangedRoles.filter((item) => !isEqual(item, state[item.id]));
};
},
onSuccess: ({ addedOrChanged, deleted }) => {
- const state = client.getQueryData<GitLabMapping[]>(queryKey);
+ const state = client.getQueryData<DevopsRolesMapping[]>(queryKey);
if (state) {
const newData = unionBy(
addedOrChanged,
}[];
}
-interface DevopsRolesMapping {
+export interface DevopsRolesMapping {
readonly baseRole?: boolean;
readonly id: string;
permissions: {
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