mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-24 14:08:45 +00:00
Update Glossary with new roles and policy (#7067)
* Update Glossary with new roles and policy * Fix unit tests * Fix tests * Fix tests
This commit is contained in:
parent
b6309b6274
commit
503873a0a4
@ -22,6 +22,44 @@ import React from 'react';
|
||||
import { mockedAssetData, mockedGlossaries } from '../../mocks/Glossary.mock';
|
||||
import GlossaryV1 from './GlossaryV1.component';
|
||||
|
||||
jest.mock('../PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: jest.fn().mockReturnValue({
|
||||
getEntityPermission: jest.fn().mockReturnValue({
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
}),
|
||||
permissions: {
|
||||
glossaryTerm: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
},
|
||||
glossary: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/PermissionsUtils', () => ({
|
||||
checkPermission: jest.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn(),
|
||||
useParams: jest.fn().mockReturnValue({
|
||||
@ -29,18 +67,6 @@ jest.mock('react-router-dom', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../authentication/auth-provider/AuthProvider', () => {
|
||||
return {
|
||||
useAuthContext: jest.fn(() => ({
|
||||
isAuthDisabled: false,
|
||||
isAuthenticated: true,
|
||||
isProtectedRoute: jest.fn().mockReturnValue(true),
|
||||
isTourRoute: jest.fn().mockReturnValue(false),
|
||||
onLogoutHandler: jest.fn(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../components/GlossaryDetails/GlossaryDetails.component', () => {
|
||||
return jest.fn().mockReturnValue(<>Glossary-Details component</>);
|
||||
});
|
||||
@ -77,6 +103,12 @@ jest.mock('antd', () => ({
|
||||
})}
|
||||
</div>
|
||||
)),
|
||||
Button: jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }) => <button>{children}</button>),
|
||||
Tooltip: jest
|
||||
.fn()
|
||||
.mockImplementation(({ children }) => <span>{children}</span>),
|
||||
}));
|
||||
|
||||
jest.mock('../common/title-breadcrumb/title-breadcrumb.component', () =>
|
||||
|
||||
@ -12,28 +12,38 @@
|
||||
*/
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Col, Dropdown, Input, Menu, Row, Space, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
Button as ButtonAntd,
|
||||
Col,
|
||||
Dropdown,
|
||||
Input,
|
||||
Menu,
|
||||
Row,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import { cloneDeep, isEmpty } from 'lodash';
|
||||
import { GlossaryTermAssets, LoadingState } from 'Models';
|
||||
import RcTree from 'rc-tree';
|
||||
import { DataNode, EventDataNode } from 'rc-tree/lib/interface';
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||
import { TITLE_FOR_NON_ADMIN_ACTION } from '../../constants/constants';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||
import { Glossary } from '../../generated/entity/data/glossary';
|
||||
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import { Operation } from '../../generated/entity/policies/policy';
|
||||
import { useAfterMount } from '../../hooks/useAfterMount';
|
||||
import { ModifiedGlossaryData } from '../../pages/GlossaryPage/GlossaryPageV1.component';
|
||||
import { getEntityDeleteMessage, getEntityName } from '../../utils/CommonUtils';
|
||||
import { generateTreeData } from '../../utils/GlossaryUtils';
|
||||
import { checkPermission } from '../../utils/PermissionsUtils';
|
||||
import { getGlossaryPath } from '../../utils/RouterUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
import ErrorPlaceHolder from '../common/error-with-placeholder/ErrorPlaceHolder';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
import Searchbar from '../common/searchbar/Searchbar';
|
||||
import TitleBreadcrumb from '../common/title-breadcrumb/title-breadcrumb.component';
|
||||
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
|
||||
@ -43,6 +53,11 @@ import GlossaryDetails from '../GlossaryDetails/GlossaryDetails.component';
|
||||
import GlossaryTermsV1 from '../GlossaryTerms/GlossaryTermsV1.component';
|
||||
import Loader from '../Loader/Loader';
|
||||
import EntityDeleteModal from '../Modals/EntityDeleteModal/EntityDeleteModal';
|
||||
import { usePermissionProvider } from '../PermissionProvider/PermissionProvider';
|
||||
import {
|
||||
OperationPermission,
|
||||
ResourceEntity,
|
||||
} from '../PermissionProvider/PermissionProvider.interface';
|
||||
import './GlossaryV1.style.less';
|
||||
const { Title } = Typography;
|
||||
|
||||
@ -50,7 +65,6 @@ type Props = {
|
||||
assetData: GlossaryTermAssets;
|
||||
deleteStatus: LoadingState;
|
||||
isSearchResultEmpty: boolean;
|
||||
isHasAccess: boolean;
|
||||
glossaryList: ModifiedGlossaryData[];
|
||||
selectedKey: string;
|
||||
expandedKey: string[];
|
||||
@ -80,7 +94,6 @@ const GlossaryV1 = ({
|
||||
assetData,
|
||||
deleteStatus = 'initial',
|
||||
isSearchResultEmpty,
|
||||
isHasAccess,
|
||||
glossaryList,
|
||||
selectedKey,
|
||||
expandedKey,
|
||||
@ -104,8 +117,7 @@ const GlossaryV1 = ({
|
||||
onRelatedTermClick,
|
||||
currentPage,
|
||||
}: Props) => {
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
const { getEntityPermission, permissions } = usePermissionProvider();
|
||||
const treeRef = useRef<RcTree<DataNode>>(null);
|
||||
const [treeData, setTreeData] = useState<DataNode[]>([]);
|
||||
const [breadcrumb, setBreadcrumb] = useState<
|
||||
@ -124,6 +136,59 @@ const GlossaryV1 = ({
|
||||
);
|
||||
const [isNameEditing, setIsNameEditing] = useState(false);
|
||||
const [displayName, setDisplayName] = useState<string>();
|
||||
|
||||
const [glossaryPermission, setGlossaryPermission] =
|
||||
useState<OperationPermission>({} as OperationPermission);
|
||||
|
||||
const [glossaryTermPermission, setGlossaryTermPermission] =
|
||||
useState<OperationPermission>({} as OperationPermission);
|
||||
|
||||
const fetchGlossaryPermission = async () => {
|
||||
try {
|
||||
const response = await getEntityPermission(
|
||||
ResourceEntity.GLOSSARY,
|
||||
selectedData?.id as string
|
||||
);
|
||||
setGlossaryPermission(response);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchGlossaryTermPermission = async () => {
|
||||
try {
|
||||
const response = await getEntityPermission(
|
||||
ResourceEntity.GLOSSARY_TERM,
|
||||
selectedData?.id as string
|
||||
);
|
||||
setGlossaryTermPermission(response);
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
const createGlossaryPermission = useMemo(
|
||||
() =>
|
||||
checkPermission(Operation.Create, ResourceEntity.GLOSSARY, permissions),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
const createGlossaryTermPermission = useMemo(
|
||||
() =>
|
||||
checkPermission(
|
||||
Operation.Create,
|
||||
ResourceEntity.GLOSSARY_TERM,
|
||||
permissions
|
||||
),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
const editDisplayNamePermission = useMemo(() => {
|
||||
return isGlossaryActive
|
||||
? glossaryPermission.EditDisplayName
|
||||
: glossaryTermPermission.EditDisplayName;
|
||||
}, [glossaryPermission, glossaryTermPermission]);
|
||||
|
||||
/**
|
||||
* To create breadcrumb from the fqn
|
||||
* @param fqn fqn of glossary or glossary term
|
||||
@ -269,16 +334,20 @@ const GlossaryV1 = ({
|
||||
typingInterval={500}
|
||||
onSearch={handleSearchText}
|
||||
/>
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Tooltip
|
||||
title={
|
||||
createGlossaryPermission
|
||||
? 'Add Glossary'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<button
|
||||
className="tw--mt-1 tw-w-full tw-flex-center tw-gap-2 tw-py-1 tw-text-primary tw-border tw-rounded-md"
|
||||
disabled={!createGlossaryPermission}
|
||||
onClick={handleAddGlossaryClick}>
|
||||
<SVGIcons alt="plus" icon={Icons.ICON_PLUS_PRIMERY} />{' '}
|
||||
<span>Add Glossary</span>
|
||||
</button>
|
||||
</NonAdminAction>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{isSearchResultEmpty ? (
|
||||
<p className="tw-text-grey-muted tw-text-center">
|
||||
@ -311,6 +380,10 @@ const GlossaryV1 = ({
|
||||
|
||||
useEffect(() => {
|
||||
setDisplayName(selectedData?.displayName);
|
||||
if (selectedData) {
|
||||
fetchGlossaryPermission();
|
||||
fetchGlossaryTermPermission();
|
||||
}
|
||||
}, [selectedData]);
|
||||
|
||||
return glossaryList.length ? (
|
||||
@ -327,32 +400,45 @@ const GlossaryV1 = ({
|
||||
/>
|
||||
</div>
|
||||
<div className="tw-relative tw-mr-2 tw--mt-2" id="add-term-button">
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Button
|
||||
className={classNames('tw-h-8 tw-rounded tw-mb-1 tw-mr-2', {
|
||||
'tw-opacity-40': isHasAccess,
|
||||
})}
|
||||
<Tooltip
|
||||
title={
|
||||
createGlossaryTermPermission
|
||||
? 'Add Term'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<ButtonAntd
|
||||
className="tw-h-8 tw-rounded tw-mb-1 tw-mr-2"
|
||||
data-testid="add-new-tag-button"
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
disabled={!createGlossaryTermPermission}
|
||||
type="primary"
|
||||
onClick={handleAddGlossaryTermClick}>
|
||||
Add term
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Dropdown
|
||||
align={{ targetOffset: [-12, 0] }}
|
||||
overlay={manageButtonContent()}
|
||||
overlayStyle={{ width: '350px' }}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
visible={showActions}
|
||||
onVisibleChange={setShowActions}>
|
||||
</ButtonAntd>
|
||||
</Tooltip>
|
||||
|
||||
<Dropdown
|
||||
align={{ targetOffset: [-12, 0] }}
|
||||
disabled={
|
||||
isGlossaryActive
|
||||
? !glossaryPermission.Delete
|
||||
: !glossaryTermPermission.Delete
|
||||
}
|
||||
overlay={manageButtonContent()}
|
||||
overlayStyle={{ width: '350px' }}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
visible={showActions}
|
||||
onVisibleChange={setShowActions}>
|
||||
<Tooltip
|
||||
title={
|
||||
glossaryPermission.Delete
|
||||
? 'Manage Glossary'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<Button
|
||||
className="tw-rounded tw-justify-center tw-w-8 tw-h-8 glossary-manage-button tw-mb-1 tw-flex"
|
||||
data-testid="manage-button"
|
||||
disabled={isHasAccess}
|
||||
disabled={!glossaryPermission.Delete}
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="outlined"
|
||||
@ -361,8 +447,8 @@ const GlossaryV1 = ({
|
||||
<FontAwesomeIcon icon="ellipsis-vertical" />
|
||||
</span>
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</NonAdminAction>
|
||||
</Tooltip>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{isChildLoading ? (
|
||||
@ -411,14 +497,24 @@ const GlossaryV1 = ({
|
||||
) : (
|
||||
<Space className="display-name">
|
||||
<Title level={4}>{getEntityName(selectedData)}</Title>
|
||||
<button onClick={() => setIsNameEditing(true)}>
|
||||
<SVGIcons
|
||||
alt="icon-tag"
|
||||
className="tw-mx-1"
|
||||
icon={Icons.EDIT}
|
||||
width="16"
|
||||
/>
|
||||
</button>
|
||||
<Tooltip
|
||||
title={
|
||||
editDisplayNamePermission
|
||||
? 'Edit Displayname'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<ButtonAntd
|
||||
disabled={!editDisplayNamePermission}
|
||||
type="text"
|
||||
onClick={() => setIsNameEditing(true)}>
|
||||
<SVGIcons
|
||||
alt="icon-tag"
|
||||
className="tw-mx-1"
|
||||
icon={Icons.EDIT}
|
||||
width="16"
|
||||
/>
|
||||
</ButtonAntd>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
)}
|
||||
</div>
|
||||
@ -427,7 +523,7 @@ const GlossaryV1 = ({
|
||||
<GlossaryDetails
|
||||
glossary={selectedData as Glossary}
|
||||
handleUserRedirection={handleUserRedirection}
|
||||
isHasAccess={isHasAccess}
|
||||
permissions={glossaryPermission}
|
||||
updateGlossary={updateGlossary}
|
||||
/>
|
||||
) : (
|
||||
@ -437,7 +533,7 @@ const GlossaryV1 = ({
|
||||
glossaryTerm={selectedData as GlossaryTerm}
|
||||
handleGlossaryTermUpdate={handleGlossaryTermUpdate}
|
||||
handleUserRedirection={handleUserRedirection}
|
||||
isHasAccess={isHasAccess}
|
||||
permissions={glossaryTermPermission}
|
||||
onAssetPaginate={onAssetPaginate}
|
||||
onRelatedTermClick={onRelatedTermClick}
|
||||
/>
|
||||
@ -460,19 +556,16 @@ const GlossaryV1 = ({
|
||||
<ErrorPlaceHolder>
|
||||
<p className="tw-text-center">No glossaries found</p>
|
||||
<p className="tw-text-center">
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<Button
|
||||
className={classNames('tw-h-8 tw-rounded tw-my-3', {
|
||||
'tw-opacity-40': !isAdminUser && !isAuthDisabled,
|
||||
})}
|
||||
data-testid="add-webhook-button"
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleAddGlossaryClick}>
|
||||
Add New Glossary
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
<Button
|
||||
className="tw-h-8 tw-rounded tw-my-3"
|
||||
data-testid="add-webhook-button"
|
||||
disabled={!createGlossaryPermission}
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="contained"
|
||||
onClick={handleAddGlossaryClick}>
|
||||
Add New Glossary
|
||||
</Button>
|
||||
</p>
|
||||
</ErrorPlaceHolder>
|
||||
</PageLayout>
|
||||
|
||||
@ -12,29 +12,20 @@
|
||||
*/
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Card as AntdCard } from 'antd';
|
||||
import { Button as ButtonAntd, Card as AntdCard, Tooltip } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import { cloneDeep, debounce, includes, isEqual } from 'lodash';
|
||||
import { EntityTags, FormattedUsersData } from 'Models';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import { WILD_CARD_CHAR } from '../../constants/char.constants';
|
||||
import {
|
||||
TITLE_FOR_NON_ADMIN_ACTION,
|
||||
TITLE_FOR_NON_OWNER_ACTION,
|
||||
TITLE_FOR_UPDATE_OWNER,
|
||||
} from '../../constants/constants';
|
||||
import { EntityType } from '../../enums/entity.enum';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||
import { Glossary } from '../../generated/entity/data/glossary';
|
||||
import { Operation } from '../../generated/entity/policies/policy';
|
||||
import { EntityReference } from '../../generated/type/entityReference';
|
||||
import { LabelType, State, TagSource } from '../../generated/type/tagLabel';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { getEntityName, hasEditAccess } from '../../utils/CommonUtils';
|
||||
import { getEntityName } from '../../utils/CommonUtils';
|
||||
import { getOwnerList } from '../../utils/ManageUtils';
|
||||
import { hasPemission } from '../../utils/PermissionsUtils';
|
||||
import SVGIcons, { Icons } from '../../utils/SvgUtils';
|
||||
import {
|
||||
getTagCategories,
|
||||
@ -47,27 +38,24 @@ import {
|
||||
searchFormattedUsersAndTeams,
|
||||
suggestFormattedUsersAndTeams,
|
||||
} from '../../utils/UserDataUtils';
|
||||
import { Button } from '../buttons/Button/Button';
|
||||
import Card from '../common/Card/Card';
|
||||
import DescriptionV1 from '../common/description/DescriptionV1';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
import ProfilePicture from '../common/ProfilePicture/ProfilePicture';
|
||||
import DropDownList from '../dropdown/DropDownList';
|
||||
import ReviewerModal from '../Modals/ReviewerModal/ReviewerModal.component';
|
||||
import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import TagsContainer from '../tags-container/tags-container';
|
||||
import TagsViewer from '../tags-viewer/tags-viewer';
|
||||
import Tags from '../tags/tags';
|
||||
|
||||
type props = {
|
||||
isHasAccess: boolean;
|
||||
permissions: OperationPermission;
|
||||
glossary: Glossary;
|
||||
updateGlossary: (value: Glossary) => void;
|
||||
handleUserRedirection?: (name: string) => void;
|
||||
};
|
||||
|
||||
const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
||||
const { userPermissions } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
const GlossaryDetails = ({ permissions, glossary, updateGlossary }: props) => {
|
||||
const [isDescriptionEditable, setIsDescriptionEditable] = useState(false);
|
||||
const [isTagEditable, setIsTagEditable] = useState<boolean>(false);
|
||||
const [tagList, setTagList] = useState<Array<string>>([]);
|
||||
@ -264,13 +252,6 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
||||
setListVisible(false);
|
||||
};
|
||||
|
||||
const isOwner = () => {
|
||||
return hasEditAccess(
|
||||
glossary?.owner?.type || '',
|
||||
glossary?.owner?.id || ''
|
||||
);
|
||||
};
|
||||
|
||||
const handleTagContainerClick = () => {
|
||||
if (!isTagEditable) {
|
||||
fetchTags();
|
||||
@ -293,44 +274,39 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
||||
|
||||
const AddReviewerButton = () => {
|
||||
return (
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<button
|
||||
className="tw-text-primary"
|
||||
<Tooltip
|
||||
placement="topRight"
|
||||
title={permissions.EditAll ? 'Add Reviewer' : NO_PERMISSION_FOR_ACTION}>
|
||||
<ButtonAntd
|
||||
className="tw-p-0"
|
||||
data-testid="add-new-reviewer"
|
||||
disabled={isHasAccess}
|
||||
disabled={!permissions.EditAll}
|
||||
type="text"
|
||||
onClick={() => setShowRevieweModal(true)}>
|
||||
<SVGIcons alt="edit" icon={Icons.EDIT} title="Edit" width="16px" />
|
||||
</button>
|
||||
</NonAdminAction>
|
||||
</ButtonAntd>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
const ownerAction = () => {
|
||||
return (
|
||||
<span className="tw-relative">
|
||||
<NonAdminAction
|
||||
html={<p>{TITLE_FOR_UPDATE_OWNER}</p>}
|
||||
isOwner={isOwner()}
|
||||
permission={Operation.EditOwner}
|
||||
position="left">
|
||||
<Button
|
||||
<Tooltip
|
||||
placement="topRight"
|
||||
title={
|
||||
permissions.EditOwner ? 'Update Owner' : NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<ButtonAntd
|
||||
className="tw-p-0"
|
||||
data-testid="owner-dropdown"
|
||||
disabled={
|
||||
!hasPemission(
|
||||
Operation.EditOwner,
|
||||
EntityType.GLOSSARY,
|
||||
userPermissions
|
||||
) &&
|
||||
!isAuthDisabled &&
|
||||
!hasEditAccess
|
||||
}
|
||||
size="custom"
|
||||
theme="primary"
|
||||
variant="text"
|
||||
disabled={!permissions.EditOwner}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={handleSelectOwnerDropdown}>
|
||||
<SVGIcons alt="edit" icon={Icons.EDIT} title="Edit" width="16px" />
|
||||
</Button>
|
||||
</NonAdminAction>
|
||||
</ButtonAntd>
|
||||
</Tooltip>
|
||||
{listVisible && (
|
||||
<DropDownList
|
||||
horzPosRight
|
||||
@ -379,20 +355,24 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
||||
<span>{getEntityName(term)}</span>
|
||||
</div>
|
||||
<span>
|
||||
<NonAdminAction
|
||||
html={<p>{TITLE_FOR_NON_OWNER_ACTION}</p>}
|
||||
isOwner={isOwner()}
|
||||
position="bottom">
|
||||
<span
|
||||
className={classNames('tw-h-8 tw-rounded tw-mb-3')}
|
||||
data-testid="remove"
|
||||
onClick={() => handleRemoveReviewer(term.id)}>
|
||||
<FontAwesomeIcon
|
||||
className="tw-cursor-pointer"
|
||||
icon="remove"
|
||||
/>
|
||||
</span>
|
||||
</NonAdminAction>
|
||||
<Tooltip
|
||||
title={
|
||||
permissions.EditAll
|
||||
? 'Remove Reviewer'
|
||||
: NO_PERMISSION_FOR_ACTION
|
||||
}>
|
||||
<ButtonAntd disabled={!permissions.EditAll} type="text">
|
||||
<span
|
||||
className={classNames('tw-h-8 tw-rounded tw-mb-3')}
|
||||
data-testid="remove"
|
||||
onClick={() => handleRemoveReviewer(term.id)}>
|
||||
<FontAwesomeIcon
|
||||
className="tw-cursor-pointer"
|
||||
icon="remove"
|
||||
/>
|
||||
</span>
|
||||
</ButtonAntd>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@ -426,52 +406,51 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<NonAdminAction
|
||||
isOwner={Boolean(glossary.owner)}
|
||||
permission={Operation.EditTags}
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_OWNER_ACTION}
|
||||
trigger="click">
|
||||
<div className="tw-inline-block" onClick={handleTagContainerClick}>
|
||||
<TagsContainer
|
||||
buttonContainerClass="tw--mt-0"
|
||||
containerClass="tw-flex tw-items-center tw-gap-2"
|
||||
dropDownHorzPosRight={false}
|
||||
editable={isTagEditable}
|
||||
isLoading={isTagLoading}
|
||||
selectedTags={getSelectedTags()}
|
||||
showTags={false}
|
||||
size="small"
|
||||
tagList={getTagOptionsFromFQN(tagList)}
|
||||
type="label"
|
||||
onCancel={() => {
|
||||
handleTagSelection();
|
||||
}}
|
||||
onSelectionChange={(tags) => {
|
||||
handleTagSelection(tags);
|
||||
}}>
|
||||
{glossary?.tags && glossary?.tags.length ? (
|
||||
<button className=" tw-ml-1 focus:tw-outline-none">
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon="icon-edit"
|
||||
title="Edit"
|
||||
width="16px"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<span>
|
||||
<Tags
|
||||
className="tw-text-primary"
|
||||
startWith="+ "
|
||||
tag="Add tag"
|
||||
type="label"
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</TagsContainer>
|
||||
</div>
|
||||
</NonAdminAction>
|
||||
|
||||
<div className="tw-inline-block" onClick={handleTagContainerClick}>
|
||||
<TagsContainer
|
||||
buttonContainerClass="tw--mt-0"
|
||||
containerClass="tw-flex tw-items-center tw-gap-2"
|
||||
dropDownHorzPosRight={false}
|
||||
editable={isTagEditable}
|
||||
isLoading={isTagLoading}
|
||||
selectedTags={getSelectedTags()}
|
||||
showTags={false}
|
||||
size="small"
|
||||
tagList={getTagOptionsFromFQN(tagList)}
|
||||
type="label"
|
||||
onCancel={() => {
|
||||
handleTagSelection();
|
||||
}}
|
||||
onSelectionChange={(tags) => {
|
||||
handleTagSelection(tags);
|
||||
}}>
|
||||
{glossary?.tags && glossary?.tags.length ? (
|
||||
<button
|
||||
className=" tw-ml-1 focus:tw-outline-none"
|
||||
disabled={!permissions.EditTags}>
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon="icon-edit"
|
||||
title="Edit"
|
||||
width="16px"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<ButtonAntd
|
||||
className="tw-p-0"
|
||||
disabled={!permissions.EditTags}
|
||||
type="text">
|
||||
<Tags
|
||||
className="tw-text-primary"
|
||||
startWith="+ "
|
||||
tag="Add tag"
|
||||
type="label"
|
||||
/>
|
||||
</ButtonAntd>
|
||||
)}
|
||||
</TagsContainer>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tw-flex tw-gap-3">
|
||||
<div className="tw-w-9/12">
|
||||
@ -481,6 +460,7 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
|
||||
removeBlur
|
||||
description={glossary?.description}
|
||||
entityName={glossary?.displayName ?? glossary?.name}
|
||||
hasEditAccess={permissions.EditDescription}
|
||||
isEdit={isDescriptionEditable}
|
||||
onCancel={onCancel}
|
||||
onDescriptionEdit={onDescriptionEdit}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
import { findByText, getByTestId, render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { mockedGlossaries } from '../../mocks/Glossary.mock';
|
||||
import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import GlossaryDetails from './GlossaryDetails.component';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
@ -61,7 +62,15 @@ jest.mock('../common/ProfilePicture/ProfilePicture', () => {
|
||||
|
||||
const mockProps = {
|
||||
glossary: mockedGlossaries[0],
|
||||
isHasAccess: true,
|
||||
permissions: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
} as OperationPermission,
|
||||
updateGlossary: jest.fn(),
|
||||
};
|
||||
|
||||
|
||||
@ -17,8 +17,47 @@ import {
|
||||
mockedAssetData,
|
||||
mockedGlossaryTerms,
|
||||
} from '../../mocks/Glossary.mock';
|
||||
import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import GlossaryTerms from './GlossaryTermsV1.component';
|
||||
|
||||
jest.mock('../PermissionProvider/PermissionProvider', () => ({
|
||||
usePermissionProvider: jest.fn().mockReturnValue({
|
||||
getEntityPermission: jest.fn().mockReturnValue({
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
}),
|
||||
permissions: {
|
||||
glossaryTerm: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
},
|
||||
glossary: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../utils/PermissionsUtils', () => ({
|
||||
checkPermission: jest.fn().mockReturnValue(true),
|
||||
}));
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
useHistory: jest.fn(),
|
||||
useParams: jest.fn().mockReturnValue({
|
||||
@ -26,18 +65,6 @@ jest.mock('react-router-dom', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../authentication/auth-provider/AuthProvider', () => {
|
||||
return {
|
||||
useAuthContext: jest.fn(() => ({
|
||||
isAuthDisabled: false,
|
||||
isAuthenticated: true,
|
||||
isProtectedRoute: jest.fn().mockReturnValue(true),
|
||||
isTourRoute: jest.fn().mockReturnValue(false),
|
||||
onLogoutHandler: jest.fn(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../components/tags-container/tags-container', () => {
|
||||
return jest.fn().mockReturnValue(<>Tags-container component</>);
|
||||
});
|
||||
@ -99,10 +126,22 @@ jest.mock('antd', () => ({
|
||||
)),
|
||||
}));
|
||||
|
||||
jest.mock('./SummaryDetail', () =>
|
||||
jest.fn().mockReturnValue(<div>SummaryDetails</div>)
|
||||
);
|
||||
|
||||
const mockProps = {
|
||||
assetData: mockedAssetData,
|
||||
currentPage: 1,
|
||||
isHasAccess: true,
|
||||
permissions: {
|
||||
Create: true,
|
||||
Delete: true,
|
||||
ViewAll: true,
|
||||
EditAll: true,
|
||||
EditDescription: true,
|
||||
EditDisplayName: true,
|
||||
EditCustomFields: true,
|
||||
} as OperationPermission,
|
||||
glossaryTerm: mockedGlossaryTerms[0],
|
||||
handleGlossaryTermUpdate: jest.fn(),
|
||||
onAssetPaginate: jest.fn(),
|
||||
|
||||
@ -20,19 +20,12 @@ import {
|
||||
Input,
|
||||
Row,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
cloneDeep,
|
||||
includes,
|
||||
isEmpty,
|
||||
isEqual,
|
||||
isString,
|
||||
isUndefined,
|
||||
kebabCase,
|
||||
} from 'lodash';
|
||||
import { cloneDeep, includes, isEmpty, isEqual } from 'lodash';
|
||||
import {
|
||||
EntityTags,
|
||||
FormattedGlossaryTermData,
|
||||
@ -40,10 +33,7 @@ import {
|
||||
GlossaryTermAssets,
|
||||
} from 'Models';
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
import {
|
||||
TITLE_FOR_NON_ADMIN_ACTION,
|
||||
TITLE_FOR_NON_OWNER_ACTION,
|
||||
} from '../../constants/constants';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||
import {
|
||||
GlossaryTerm,
|
||||
TermReference,
|
||||
@ -59,21 +49,22 @@ import {
|
||||
} from '../../utils/TagsUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
import DescriptionV1 from '../common/description/DescriptionV1';
|
||||
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
|
||||
import ProfilePicture from '../common/ProfilePicture/ProfilePicture';
|
||||
import TabsPane from '../common/TabsPane/TabsPane';
|
||||
import GlossaryReferenceModal from '../Modals/GlossaryReferenceModal/GlossaryReferenceModal';
|
||||
import RelatedTermsModal from '../Modals/RelatedTermsModal/RelatedTermsModal';
|
||||
import ReviewerModal from '../Modals/ReviewerModal/ReviewerModal.component';
|
||||
import { OperationPermission } from '../PermissionProvider/PermissionProvider.interface';
|
||||
import TagsContainer from '../tags-container/tags-container';
|
||||
import TagsViewer from '../tags-viewer/tags-viewer';
|
||||
import Tags from '../tags/tags';
|
||||
import SummaryDetail from './SummaryDetail';
|
||||
import AssetsTabs from './tabs/AssetsTabs.component';
|
||||
const { Text } = Typography;
|
||||
|
||||
type Props = {
|
||||
assetData: GlossaryTermAssets;
|
||||
isHasAccess: boolean;
|
||||
permissions: OperationPermission;
|
||||
glossaryTerm: GlossaryTerm;
|
||||
currentPage: number;
|
||||
handleGlossaryTermUpdate: (data: GlossaryTerm) => void;
|
||||
@ -82,21 +73,14 @@ type Props = {
|
||||
handleUserRedirection?: (name: string) => void;
|
||||
};
|
||||
|
||||
type SummaryDetailsProps = {
|
||||
title: string;
|
||||
children: React.ReactElement;
|
||||
setShow?: (value: React.SetStateAction<boolean>) => void;
|
||||
data?: FormattedGlossaryTermData[] | TermReference[] | string;
|
||||
};
|
||||
|
||||
const GlossaryTermsV1 = ({
|
||||
assetData,
|
||||
isHasAccess,
|
||||
glossaryTerm,
|
||||
handleGlossaryTermUpdate,
|
||||
onAssetPaginate,
|
||||
onRelatedTermClick,
|
||||
currentPage,
|
||||
permissions,
|
||||
}: Props) => {
|
||||
const [isTagEditable, setIsTagEditable] = useState<boolean>(false);
|
||||
const [tagList, setTagList] = useState<Array<string>>([]);
|
||||
@ -301,7 +285,7 @@ const GlossaryTermsV1 = ({
|
||||
const handleValidation = (
|
||||
event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
||||
) => {
|
||||
if (isHasAccess) {
|
||||
if (permissions.EditAll) {
|
||||
return;
|
||||
}
|
||||
const value = event.target.value;
|
||||
@ -344,28 +328,18 @@ const GlossaryTermsV1 = ({
|
||||
|
||||
const addReviewerButton = () => {
|
||||
return (
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<button
|
||||
className="tw-text-primary tw-flex tw-items-center"
|
||||
<Tooltip
|
||||
placement="topRight"
|
||||
title={permissions.EditAll ? 'Add Reviewer' : NO_PERMISSION_FOR_ACTION}>
|
||||
<Button
|
||||
className="tw-p-0"
|
||||
data-testid="add-new-reviewer"
|
||||
disabled={isHasAccess}
|
||||
disabled={!permissions.EditAll}
|
||||
type="text"
|
||||
onClick={() => setShowRevieweModal(true)}>
|
||||
<SVGIcons alt="edit" icon={Icons.EDIT} title="Edit" width="16px" />
|
||||
</button>
|
||||
</NonAdminAction>
|
||||
);
|
||||
};
|
||||
|
||||
const addButton = (onClick: () => void) => {
|
||||
return (
|
||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||
<span
|
||||
className="tw-cursor-pointer"
|
||||
data-testid="add-button"
|
||||
onClick={onClick}>
|
||||
<SVGIcons alt="icon-plus-primary" icon="icon-plus-primary-outlined" />
|
||||
</span>
|
||||
</NonAdminAction>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@ -398,9 +372,7 @@ const GlossaryTermsV1 = ({
|
||||
<span>{getEntityName(term)}</span>
|
||||
</div>
|
||||
<span>
|
||||
<NonAdminAction
|
||||
html={<p>{TITLE_FOR_NON_OWNER_ACTION}</p>}
|
||||
position="bottom">
|
||||
<Button disabled={!permissions.EditAll} type="text">
|
||||
<span
|
||||
className={classNames('tw-h-8 tw-rounded tw-mb-3')}
|
||||
data-testid="remove"
|
||||
@ -410,7 +382,7 @@ const GlossaryTermsV1 = ({
|
||||
icon="remove"
|
||||
/>
|
||||
</span>
|
||||
</NonAdminAction>
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
@ -437,34 +409,6 @@ const GlossaryTermsV1 = ({
|
||||
);
|
||||
};
|
||||
|
||||
const SummaryDetail = ({
|
||||
title,
|
||||
children,
|
||||
setShow,
|
||||
data,
|
||||
...props
|
||||
}: SummaryDetailsProps) => {
|
||||
return (
|
||||
<Space direction="vertical" {...props}>
|
||||
<Space>
|
||||
<Text type="secondary">{title}</Text>
|
||||
<div className="tw-ml-2" data-testid={`section-${kebabCase(title)}`}>
|
||||
{addButton(() => setShow && setShow(true))}
|
||||
</div>
|
||||
</Space>
|
||||
{!isString(data) && !isUndefined(data) && data.length > 0 ? (
|
||||
<div
|
||||
className="tw-flex"
|
||||
data-testid={`${kebabCase(title)}-container`}>
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
<div data-testid={`${kebabCase(title)}-container`}>{children}</div>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
const SummaryTab = () => {
|
||||
return (
|
||||
<Row gutter={16}>
|
||||
@ -482,6 +426,7 @@ const GlossaryTermsV1 = ({
|
||||
<Divider className="m-r-1" />
|
||||
<SummaryDetail
|
||||
data={relatedTerms}
|
||||
hasAccess={permissions.EditAll}
|
||||
key="related_term"
|
||||
setShow={setShowRelatedTermsModal}
|
||||
title="Related Terms">
|
||||
@ -510,6 +455,7 @@ const GlossaryTermsV1 = ({
|
||||
<Divider className="m-r-1" />
|
||||
|
||||
<SummaryDetail
|
||||
hasAccess={permissions.EditAll}
|
||||
key="synonyms"
|
||||
setShow={setIsSynonymsEditing}
|
||||
title="Synonyms">
|
||||
@ -558,6 +504,7 @@ const GlossaryTermsV1 = ({
|
||||
|
||||
<SummaryDetail
|
||||
data={references}
|
||||
hasAccess={permissions.EditAll}
|
||||
key="references"
|
||||
setShow={setIsReferencesEditing}
|
||||
title="References">
|
||||
@ -629,50 +576,49 @@ const GlossaryTermsV1 = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<NonAdminAction
|
||||
position="bottom"
|
||||
title={TITLE_FOR_NON_ADMIN_ACTION}
|
||||
trigger="click">
|
||||
<div className="tw-inline-block" onClick={handleTagContainerClick}>
|
||||
<TagsContainer
|
||||
buttonContainerClass="tw--mt-0"
|
||||
containerClass="tw-flex tw-items-center tw-gap-2"
|
||||
dropDownHorzPosRight={false}
|
||||
editable={isTagEditable}
|
||||
isLoading={isTagLoading}
|
||||
selectedTags={getSelectedTags()}
|
||||
showTags={false}
|
||||
size="small"
|
||||
tagList={getTagOptionsFromFQN(tagList)}
|
||||
type="label"
|
||||
onCancel={() => {
|
||||
handleTagSelection();
|
||||
}}
|
||||
onSelectionChange={(tags) => {
|
||||
handleTagSelection(tags);
|
||||
}}>
|
||||
{glossaryTerm?.tags && glossaryTerm?.tags.length ? (
|
||||
<button className="tw-ml-1 focus:tw-outline-none">
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon="icon-edit"
|
||||
title="Edit"
|
||||
width="16px"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<span>
|
||||
<Tags
|
||||
className="tw-text-primary"
|
||||
startWith="+ "
|
||||
tag="Add tag"
|
||||
type="label"
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</TagsContainer>
|
||||
</div>
|
||||
</NonAdminAction>
|
||||
|
||||
<div className="tw-inline-block" onClick={handleTagContainerClick}>
|
||||
<TagsContainer
|
||||
buttonContainerClass="tw--mt-0"
|
||||
containerClass="tw-flex tw-items-center tw-gap-2"
|
||||
dropDownHorzPosRight={false}
|
||||
editable={isTagEditable}
|
||||
isLoading={isTagLoading}
|
||||
selectedTags={getSelectedTags()}
|
||||
showTags={false}
|
||||
size="small"
|
||||
tagList={getTagOptionsFromFQN(tagList)}
|
||||
type="label"
|
||||
onCancel={() => {
|
||||
handleTagSelection();
|
||||
}}
|
||||
onSelectionChange={(tags) => {
|
||||
handleTagSelection(tags);
|
||||
}}>
|
||||
{glossaryTerm?.tags && glossaryTerm?.tags.length ? (
|
||||
<button className="tw-ml-1 focus:tw-outline-none">
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
icon="icon-edit"
|
||||
title="Edit"
|
||||
width="16px"
|
||||
/>
|
||||
</button>
|
||||
) : (
|
||||
<Button
|
||||
className="tw-p-0"
|
||||
disabled={!permissions.EditTags}
|
||||
type="text">
|
||||
<Tags
|
||||
className="tw-text-primary"
|
||||
startWith="+ "
|
||||
tag="Add tag"
|
||||
type="label"
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
</TagsContainer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="tw-flex tw-flex-col tw-flex-grow">
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
import { Button, Space, Tooltip, Typography } from 'antd';
|
||||
import { isString, isUndefined, kebabCase } from 'lodash';
|
||||
import { FormattedGlossaryTermData } from 'Models';
|
||||
import React from 'react';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil';
|
||||
import { TermReference } from '../../generated/entity/data/glossaryTerm';
|
||||
import SVGIcons from '../../utils/SvgUtils';
|
||||
|
||||
interface SummaryDetailsProps {
|
||||
title: string;
|
||||
children: React.ReactElement;
|
||||
hasAccess: boolean;
|
||||
setShow?: (value: React.SetStateAction<boolean>) => void;
|
||||
data?: FormattedGlossaryTermData[] | TermReference[] | string;
|
||||
}
|
||||
|
||||
const SummaryDetail = ({
|
||||
title,
|
||||
children,
|
||||
setShow,
|
||||
data,
|
||||
hasAccess,
|
||||
...props
|
||||
}: SummaryDetailsProps) => {
|
||||
return (
|
||||
<Space direction="vertical" {...props}>
|
||||
<Space>
|
||||
<Typography.Text type="secondary">{title}</Typography.Text>
|
||||
<div className="tw-ml-2" data-testid={`section-${kebabCase(title)}`}>
|
||||
<Tooltip title={hasAccess ? 'Add' : NO_PERMISSION_FOR_ACTION}>
|
||||
<Button
|
||||
className="tw-cursor-pointer"
|
||||
data-testid="add-button"
|
||||
disabled={!hasAccess}
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={() => setShow && setShow(true)}>
|
||||
<SVGIcons
|
||||
alt="icon-plus-primary"
|
||||
icon="icon-plus-primary-outlined"
|
||||
/>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Space>
|
||||
{!isString(data) && !isUndefined(data) && data.length > 0 ? (
|
||||
<div className="tw-flex" data-testid={`${kebabCase(title)}-container`}>
|
||||
{children}
|
||||
</div>
|
||||
) : (
|
||||
<div data-testid={`${kebabCase(title)}-container`}>{children}</div>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export default SummaryDetail;
|
||||
@ -11,19 +11,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Space, Typography } from 'antd';
|
||||
import { Space, Tooltip, Typography } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { EntityFieldThreads } from 'Models';
|
||||
import React, { Fragment } from 'react';
|
||||
import { EntityField } from '../../../constants/feed.constants';
|
||||
import { NO_PERMISSION_FOR_ACTION } from '../../../constants/HelperTextUtil';
|
||||
import { Table } from '../../../generated/entity/data/table';
|
||||
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
||||
import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils';
|
||||
import { getEntityFeedLink } from '../../../utils/EntityUtils';
|
||||
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
|
||||
import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
|
||||
import NonAdminAction from '../non-admin-action/NonAdminAction';
|
||||
import PopOver from '../popover/PopOver';
|
||||
import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer';
|
||||
const { Text } = Typography;
|
||||
@ -47,7 +45,6 @@ interface Props {
|
||||
onEntityFieldSelect?: (value: string) => void;
|
||||
}
|
||||
const DescriptionV1 = ({
|
||||
owner,
|
||||
hasEditAccess,
|
||||
onDescriptionEdit,
|
||||
description = '',
|
||||
@ -67,14 +64,12 @@ const DescriptionV1 = ({
|
||||
|
||||
const editButton = () => {
|
||||
return !isReadOnly ? (
|
||||
<NonAdminAction
|
||||
html={getHtmlForNonAdminAction(Boolean(owner))}
|
||||
isOwner={hasEditAccess}
|
||||
permission={Operation.EditDescription}
|
||||
position="right">
|
||||
<Tooltip
|
||||
title={hasEditAccess ? 'Edit Description' : NO_PERMISSION_FOR_ACTION}>
|
||||
<button
|
||||
className="focus:tw-outline-none tw-text-primary"
|
||||
data-testid="edit-description"
|
||||
disabled={!hasEditAccess}
|
||||
onClick={onDescriptionEdit}>
|
||||
<SVGIcons
|
||||
alt="edit"
|
||||
@ -83,7 +78,7 @@ const DescriptionV1 = ({
|
||||
width="16px"
|
||||
/>
|
||||
</button>
|
||||
</NonAdminAction>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
@ -23,7 +23,6 @@ import {
|
||||
} from 'Models';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useHistory, useParams } from 'react-router-dom';
|
||||
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
|
||||
import {
|
||||
deleteGlossary,
|
||||
deleteGlossaryTerm,
|
||||
@ -42,7 +41,6 @@ import { myDataSearchIndex } from '../../constants/Mydata.constants';
|
||||
import { SearchIndex } from '../../enums/search.enum';
|
||||
import { Glossary } from '../../generated/entity/data/glossary';
|
||||
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
|
||||
import { useAuth } from '../../hooks/authHooks';
|
||||
import jsonData from '../../jsons/en';
|
||||
import { formatDataResponse } from '../../utils/APIUtils';
|
||||
import {
|
||||
@ -66,8 +64,6 @@ export type ModifiedGlossaryData = Glossary & {
|
||||
const GlossaryPageV1 = () => {
|
||||
const { glossaryName } = useParams<Record<string, string>>();
|
||||
|
||||
const { isAdminUser } = useAuth();
|
||||
const { isAuthDisabled } = useAuthContext();
|
||||
const history = useHistory();
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [isChildLoading, setIsChildLoading] = useState(true);
|
||||
@ -724,7 +720,6 @@ const GlossaryPageV1 = () => {
|
||||
handleUserRedirection={handleUserRedirection}
|
||||
isChildLoading={isChildLoading}
|
||||
isGlossaryActive={isGlossaryActive}
|
||||
isHasAccess={!isAdminUser && !isAuthDisabled}
|
||||
isSearchResultEmpty={isSearchResultEmpty}
|
||||
loadingKey={loadingKey}
|
||||
searchText={searchText}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import React, { FunctionComponent, useMemo } from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import AppState from '../AppState';
|
||||
import { usePermissionProvider } from '../components/PermissionProvider/PermissionProvider';
|
||||
@ -212,6 +212,32 @@ const EditRulePage = withSuspenseFallback(
|
||||
const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
const { permissions } = usePermissionProvider();
|
||||
|
||||
const glossaryPermission = useMemo(
|
||||
() =>
|
||||
checkPermission(Operation.ViewAll, ResourceEntity.GLOSSARY, permissions),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
const glossaryTermPermission = useMemo(
|
||||
() =>
|
||||
checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.GLOSSARY_TERM,
|
||||
permissions
|
||||
),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
const tagCategoryPermission = useMemo(
|
||||
() =>
|
||||
checkPermission(
|
||||
Operation.ViewAll,
|
||||
ResourceEntity.TAG_CATEGORY,
|
||||
permissions
|
||||
),
|
||||
[permissions]
|
||||
);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route exact component={MyDataPage} path={ROUTES.MY_DATA} />
|
||||
@ -251,8 +277,18 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
{!isEmpty(AppState.userDetails) && <Redirect to={ROUTES.HOME} />}
|
||||
</Route>
|
||||
<Route exact component={SwaggerPage} path={ROUTES.SWAGGER} />
|
||||
<Route exact component={TagsPage} path={ROUTES.TAGS} />
|
||||
<Route exact component={TagsPage} path={ROUTES.TAG_DETAILS} />
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={TagsPage}
|
||||
hasPermission={tagCategoryPermission}
|
||||
path={ROUTES.TAGS}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={TagsPage}
|
||||
hasPermission={tagCategoryPermission}
|
||||
path={ROUTES.TAG_DETAILS}
|
||||
/>
|
||||
<Route exact component={DatabaseDetails} path={ROUTES.DATABASE_DETAILS} />
|
||||
<Route
|
||||
exact
|
||||
@ -303,9 +339,24 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
||||
/>
|
||||
<Route exact component={EntityVersionPage} path={ROUTES.ENTITY_VERSION} />
|
||||
<Route exact component={EditWebhookPage} path={ROUTES.EDIT_WEBHOOK} />
|
||||
<Route exact component={GlossaryPageV1} path={ROUTES.GLOSSARY} />
|
||||
<Route exact component={GlossaryPageV1} path={ROUTES.GLOSSARY_DETAILS} />
|
||||
<Route exact component={GlossaryPageV1} path={ROUTES.GLOSSARY_TERMS} />
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={GlossaryPageV1}
|
||||
hasPermission={glossaryPermission}
|
||||
path={ROUTES.GLOSSARY}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={GlossaryPageV1}
|
||||
hasPermission={glossaryPermission}
|
||||
path={ROUTES.GLOSSARY_DETAILS}
|
||||
/>
|
||||
<AdminProtectedRoute
|
||||
exact
|
||||
component={GlossaryPageV1}
|
||||
hasPermission={glossaryTermPermission}
|
||||
path={ROUTES.GLOSSARY_TERMS}
|
||||
/>
|
||||
<Route exact component={UserPage} path={ROUTES.USER_PROFILE} />
|
||||
<Route exact component={UserPage} path={ROUTES.USER_PROFILE_WITH_TAB} />
|
||||
<Route exact component={MlModelPage} path={ROUTES.MLMODEL_DETAILS} />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user