fix import export permissions (#17482)

This commit is contained in:
Karan Hotchandani 2024-08-19 15:29:07 +05:30 committed by GitHub
parent c47a46ad78
commit 4c49f098bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 71 additions and 8 deletions

View File

@ -120,6 +120,21 @@ const GlossaryHeader = ({
[globalPermissions] [globalPermissions]
); );
const importExportPermissions = useMemo(
() =>
checkPermission(
Operation.All,
ResourceEntity.GLOSSARY_TERM,
globalPermissions
) ||
checkPermission(
Operation.EditAll,
ResourceEntity.GLOSSARY_TERM,
globalPermissions
),
[globalPermissions]
);
// To fetch the latest glossary data // To fetch the latest glossary data
// necessary to handle back click functionality to work properly in version page // necessary to handle back click functionality to work properly in version page
const fetchCurrentGlossaryInfo = async () => { const fetchCurrentGlossaryInfo = async () => {
@ -298,7 +313,7 @@ const GlossaryHeader = ({
}, [selectedData]); }, [selectedData]);
const manageButtonContent: ItemType[] = [ const manageButtonContent: ItemType[] = [
...(isGlossary ...(isGlossary && importExportPermissions
? ([ ? ([
{ {
label: ( label: (

View File

@ -23,6 +23,25 @@ import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
import { QueryVoteType } from '../../Database/TableQueries/TableQueries.interface'; import { QueryVoteType } from '../../Database/TableQueries/TableQueries.interface';
import GlossaryHeader from './GlossaryHeader.component'; import GlossaryHeader from './GlossaryHeader.component';
const glossaryTermPermission = {
All: true,
Create: true,
Delete: true,
ViewAll: true,
EditAll: true,
EditDescription: true,
EditDisplayName: true,
EditCustomFields: true,
};
jest.mock('../../../context/PermissionProvider/PermissionProvider', () => ({
usePermissionProvider: jest.fn().mockImplementation(() => ({
permissions: {
glossaryTerm: glossaryTermPermission,
},
})),
}));
jest.mock('react-router-dom', () => ({ jest.mock('react-router-dom', () => ({
useHistory: jest.fn(), useHistory: jest.fn(),
useParams: jest.fn().mockReturnValue({ useParams: jest.fn().mockReturnValue({
@ -195,6 +214,24 @@ describe('GlossaryHeader component', () => {
expect(screen.queryByText('label.style')).not.toBeInTheDocument(); expect(screen.queryByText('label.style')).not.toBeInTheDocument();
}); });
it('should not render import and export dropdown menu items if no permission', async () => {
glossaryTermPermission.All = false;
glossaryTermPermission.EditAll = false;
render(
<GlossaryHeader
isGlossary
permissions={DEFAULT_ENTITY_PERMISSION}
selectedData={{ displayName: 'glossaryTest' } as Glossary}
updateVote={mockOnUpdateVote}
onAddGlossaryTerm={mockOnDelete}
onDelete={mockOnDelete}
onUpdate={mockOnUpdate}
/>
);
expect(screen.queryByTestId('manage-button')).not.toBeInTheDocument();
});
it('should render changeParentHierarchy and style dropdown menu items only for glossaryTerm', async () => { it('should render changeParentHierarchy and style dropdown menu items only for glossaryTerm', async () => {
render( render(
<GlossaryHeader <GlossaryHeader

View File

@ -74,9 +74,11 @@ export const EntityImport = ({
setUploading(true); setUploading(true);
const response = await onImport(entityName, result); const response = await onImport(entityName, result);
setCsvImportResult(response); if (response) {
setCsvFileResult(result); setCsvImportResult(response);
setActiveStep(2); setCsvFileResult(result);
setActiveStep(2);
}
} }
} catch (error) { } catch (error) {
setCsvImportResult(undefined); setCsvImportResult(undefined);

View File

@ -17,6 +17,7 @@ import i18next from 'i18next';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { ReactComponent as GlossaryTermIcon } from '../assets/svg/book.svg';
import { ReactComponent as IconChart } from '../assets/svg/chart.svg'; import { ReactComponent as IconChart } from '../assets/svg/chart.svg';
import { ReactComponent as IconDashboard } from '../assets/svg/dashboard-grey.svg'; import { ReactComponent as IconDashboard } from '../assets/svg/dashboard-grey.svg';
import { ReactComponent as IconApiCollection } from '../assets/svg/ic-api-collection-default.svg'; import { ReactComponent as IconApiCollection } from '../assets/svg/ic-api-collection-default.svg';
@ -26,7 +27,6 @@ import { ReactComponent as IconContainer } from '../assets/svg/ic-storage.svg';
import { ReactComponent as IconStoredProcedure } from '../assets/svg/ic-stored-procedure.svg'; import { ReactComponent as IconStoredProcedure } from '../assets/svg/ic-stored-procedure.svg';
import { ReactComponent as IconMlModal } from '../assets/svg/mlmodal.svg'; import { ReactComponent as IconMlModal } from '../assets/svg/mlmodal.svg';
import { ReactComponent as IconPipeline } from '../assets/svg/pipeline-grey.svg'; import { ReactComponent as IconPipeline } from '../assets/svg/pipeline-grey.svg';
import { ReactComponent as IconTable } from '../assets/svg/table-grey.svg';
import { ReactComponent as IconTag } from '../assets/svg/tag-grey.svg'; import { ReactComponent as IconTag } from '../assets/svg/tag-grey.svg';
import { ReactComponent as IconTopic } from '../assets/svg/topic-grey.svg'; import { ReactComponent as IconTopic } from '../assets/svg/topic-grey.svg';
import { import {
@ -124,7 +124,7 @@ export const getGroupLabel = (index: string) => {
break; break;
case SearchIndex.GLOSSARY_TERM: case SearchIndex.GLOSSARY_TERM:
label = i18next.t('label.glossary-term-plural'); label = i18next.t('label.glossary-term-plural');
GroupIcon = IconTable; GroupIcon = GlossaryTermIcon;
break; break;
case SearchIndex.TAG: case SearchIndex.TAG:
@ -219,11 +219,18 @@ export const getSuggestionElement = (
FqnPart.Service, FqnPart.Service,
])}-${name}`.replaceAll(`"`, ''); ])}-${name}`.replaceAll(`"`, '');
const displayText = let displayText =
database && schema database && schema
? `${database}${FQN_SEPARATOR_CHAR}${schema}${FQN_SEPARATOR_CHAR}${name}` ? `${database}${FQN_SEPARATOR_CHAR}${schema}${FQN_SEPARATOR_CHAR}${name}`
: searchClassBase.getEntityName(entitySource); : searchClassBase.getEntityName(entitySource);
if (index === SearchIndex.GLOSSARY_TERM) {
// Show Fqn for Glossary Term. Adding this to avoid confusion for nested terms
displayText =
entitySource.fullyQualifiedName ??
searchClassBase.getEntityName(entitySource);
}
const retn = ( const retn = (
<Button <Button
block block

View File

@ -51,6 +51,7 @@ export const showErrorToast = (
if (isString(error)) { if (isString(error)) {
errorMessage = error.toString(); errorMessage = error.toString();
} else { } else {
const method = error.config?.method?.toUpperCase();
const fallback = const fallback =
fallbackText && fallbackText.length > 0 fallbackText && fallbackText.length > 0
? fallbackText ? fallbackText
@ -62,7 +63,8 @@ export const showErrorToast = (
if ( if (
error && error &&
(error.response?.status === ClientErrors.UNAUTHORIZED || (error.response?.status === ClientErrors.UNAUTHORIZED ||
error.response?.status === ClientErrors.FORBIDDEN) && (error.response?.status === ClientErrors.FORBIDDEN &&
method === 'GET')) &&
!errorMessage.includes('principal domain') !errorMessage.includes('principal domain')
) { ) {
return; return;