mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 10:39:30 +00:00 
			
		
		
		
	Fix custom property permission issue (#7126)
This commit is contained in:
		
							parent
							
								
									891603e72f
								
							
						
					
					
						commit
						77c40c97f2
					
				| @ -1,180 +0,0 @@ | ||||
| /* | ||||
|  *  Copyright 2021 Collate | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  *  http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| import { fireEvent, render } from '@testing-library/react'; | ||||
| import React from 'react'; | ||||
| import { MemoryRouter } from 'react-router-dom'; | ||||
| import { Type } from '../../generated/entity/type'; | ||||
| import { Tab } from '../common/TabsPane/TabsPane'; | ||||
| import CustomEntityDetail from './CustomEntityDetail'; | ||||
| 
 | ||||
| const mockData = { | ||||
|   id: '32f81349-d7d7-4a6a-8fc7-d767f233b674', | ||||
|   name: 'table', | ||||
|   fullyQualifiedName: 'table', | ||||
|   displayName: 'table', | ||||
|   description: | ||||
|     // eslint-disable-next-line max-len
 | ||||
|     '"This schema defines the Table entity. A Table organizes data in rows and columns and is defined by a Schema. OpenMetadata does not have a separate abstraction for Schema. Both Table and Schema are captured in this entity."', | ||||
|   category: 'entity', | ||||
|   nameSpace: 'data', | ||||
|   schema: '', | ||||
|   version: 0.1, | ||||
|   updatedAt: 1653626359971, | ||||
|   updatedBy: 'admin', | ||||
|   href: 'http://localhost:8585/api/v1/metadata/types/32f81349-d7d7-4a6a-8fc7-d767f233b674', | ||||
| } as Type; | ||||
| 
 | ||||
| const mockPush = jest.fn(); | ||||
| 
 | ||||
| const MOCK_HISTORY = { | ||||
|   push: mockPush, | ||||
| }; | ||||
| 
 | ||||
| jest.mock('react-router-dom', () => ({ | ||||
|   useHistory: jest.fn().mockImplementation(() => MOCK_HISTORY), | ||||
| })); | ||||
| 
 | ||||
| jest.mock('../../axiosAPIs/metadataTypeAPI', () => ({ | ||||
|   getTypeByFQN: jest | ||||
|     .fn() | ||||
|     .mockImplementation(() => Promise.resolve({ data: mockData })), | ||||
| })); | ||||
| 
 | ||||
| jest.mock('../containers/PageContainer', () => { | ||||
|   return jest | ||||
|     .fn() | ||||
|     .mockImplementation(({ children }: { children: React.ReactNode }) => ( | ||||
|       <div>{children}</div> | ||||
|     )); | ||||
| }); | ||||
| 
 | ||||
| jest.mock('../../constants/constants', () => ({ | ||||
|   getAddCustomPropertyPath: jest.fn().mockReturnValue('/custom-entity/table'), | ||||
| })); | ||||
| 
 | ||||
| jest.mock('../common/TabsPane/TabsPane', () => | ||||
|   jest.fn().mockImplementation(({ setActiveTab, tabs }) => { | ||||
|     return ( | ||||
|       <div> | ||||
|         <nav | ||||
|           className="tw-flex tw-items-center tw-justify-between tw-gh-tabs-container tw-px-7" | ||||
|           data-testid="tabs" | ||||
|           id="tabs"> | ||||
|           {tabs.map((tab: Tab) => ( | ||||
|             <button | ||||
|               data-testid={tab.name} | ||||
|               key={tab.position} | ||||
|               onClick={() => setActiveTab?.(tab.position)}> | ||||
|               {tab.name} | ||||
|             </button> | ||||
|           ))} | ||||
|         </nav> | ||||
|       </div> | ||||
|     ); | ||||
|   }) | ||||
| ); | ||||
| 
 | ||||
| jest.mock('../schema-editor/SchemaEditor', () => | ||||
|   jest | ||||
|     .fn() | ||||
|     .mockReturnValue(<div data-testid="schema-editor">Schema Editor</div>) | ||||
| ); | ||||
| 
 | ||||
| jest.mock('./CustomPropertyTable', () => ({ | ||||
|   CustomPropertyTable: jest | ||||
|     .fn() | ||||
|     .mockReturnValue( | ||||
|       <div data-testid="CustomPropertyTable">CustomPropertyTable</div> | ||||
|     ), | ||||
| })); | ||||
| 
 | ||||
| jest.mock('./LeftPanel', () => ({ | ||||
|   LeftPanel: jest | ||||
|     .fn() | ||||
|     .mockReturnValue(<div data-testid="LeftPanel">LeftPanel</div>), | ||||
| })); | ||||
| 
 | ||||
| describe('Test Custom Entity Detail Component', () => { | ||||
|   it('Should render custom entity component', async () => { | ||||
|     const { findByTestId } = render( | ||||
|       <CustomEntityDetail entityTypes={[mockData]} />, | ||||
|       { | ||||
|         wrapper: MemoryRouter, | ||||
|       } | ||||
|     ); | ||||
| 
 | ||||
|     const leftPanel = await findByTestId('LeftPanel'); | ||||
|     const tabContainer = await findByTestId('tabs'); | ||||
| 
 | ||||
|     const schemaTab = await findByTestId('Schema'); | ||||
|     const customPropertiesTab = await findByTestId('Custom Properties'); | ||||
| 
 | ||||
|     expect(leftPanel).toBeInTheDocument(); | ||||
|     expect(tabContainer).toBeInTheDocument(); | ||||
|     expect(schemaTab).toBeInTheDocument(); | ||||
|     expect(customPropertiesTab).toBeInTheDocument(); | ||||
|   }); | ||||
| 
 | ||||
|   it('Should render custom fields table if active tab is Custom Fields', async () => { | ||||
|     const { findByTestId } = render( | ||||
|       <CustomEntityDetail entityTypes={[mockData]} />, | ||||
|       { | ||||
|         wrapper: MemoryRouter, | ||||
|       } | ||||
|     ); | ||||
| 
 | ||||
|     const leftPanel = await findByTestId('LeftPanel'); | ||||
|     const tabContainer = await findByTestId('tabs'); | ||||
| 
 | ||||
|     expect(leftPanel).toBeInTheDocument(); | ||||
|     expect(tabContainer).toBeInTheDocument(); | ||||
| 
 | ||||
|     const customPropertiesTab = await findByTestId('Custom Properties'); | ||||
| 
 | ||||
|     expect(customPropertiesTab).toBeInTheDocument(); | ||||
| 
 | ||||
|     fireEvent.click(customPropertiesTab); | ||||
| 
 | ||||
|     expect(await findByTestId('CustomPropertyTable')).toBeInTheDocument(); | ||||
|   }); | ||||
| 
 | ||||
|   it('Should call history.push method on click of Add field button', async () => { | ||||
|     const { findByTestId } = render( | ||||
|       <CustomEntityDetail entityTypes={[mockData]} />, | ||||
|       { | ||||
|         wrapper: MemoryRouter, | ||||
|       } | ||||
|     ); | ||||
| 
 | ||||
|     const tabContainer = await findByTestId('tabs'); | ||||
| 
 | ||||
|     expect(tabContainer).toBeInTheDocument(); | ||||
| 
 | ||||
|     const customPropertiesTab = await findByTestId('Custom Properties'); | ||||
| 
 | ||||
|     expect(customPropertiesTab).toBeInTheDocument(); | ||||
| 
 | ||||
|     fireEvent.click(customPropertiesTab); | ||||
| 
 | ||||
|     expect(await findByTestId('CustomPropertyTable')).toBeInTheDocument(); | ||||
| 
 | ||||
|     const addFieldButton = await findByTestId('add-field-button'); | ||||
| 
 | ||||
|     expect(addFieldButton).toBeInTheDocument(); | ||||
| 
 | ||||
|     fireEvent.click(addFieldButton); | ||||
| 
 | ||||
|     expect(mockPush).toHaveBeenCalled(); | ||||
|   }); | ||||
| }); | ||||
| @ -1,181 +0,0 @@ | ||||
| /* | ||||
|  *  Copyright 2021 Collate | ||||
|  *  Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  *  you may not use this file except in compliance with the License. | ||||
|  *  You may obtain a copy of the License at | ||||
|  *  http://www.apache.org/licenses/LICENSE-2.0
 | ||||
|  *  Unless required by applicable law or agreed to in writing, software | ||||
|  *  distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  *  See the License for the specific language governing permissions and | ||||
|  *  limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| import { AxiosError } from 'axios'; | ||||
| import { compare } from 'fast-json-patch'; | ||||
| import { isEmpty } from 'lodash'; | ||||
| import React, { FC, useEffect, useState } from 'react'; | ||||
| import { useHistory } from 'react-router-dom'; | ||||
| import { getTypeByFQN, updateType } from '../../axiosAPIs/metadataTypeAPI'; | ||||
| import { getAddCustomPropertyPath } from '../../constants/constants'; | ||||
| import { Type } from '../../generated/entity/type'; | ||||
| import jsonData from '../../jsons/en'; | ||||
| import { showErrorToast } from '../../utils/ToastUtils'; | ||||
| import { Button } from '../buttons/Button/Button'; | ||||
| import ErrorPlaceHolder from '../common/error-with-placeholder/ErrorPlaceHolder'; | ||||
| import TabsPane from '../common/TabsPane/TabsPane'; | ||||
| import PageContainer from '../containers/PageContainer'; | ||||
| import PageLayout from '../containers/PageLayout'; | ||||
| import SchemaEditor from '../schema-editor/SchemaEditor'; | ||||
| import { CustomPropertyTable } from './CustomPropertyTable'; | ||||
| import { LeftPanel } from './LeftPanel'; | ||||
| 
 | ||||
| interface Props { | ||||
|   entityTypes: Array<Type>; | ||||
|   entityTypeFQN?: string; | ||||
| } | ||||
| 
 | ||||
| const CustomEntityDetail: FC<Props> = ({ entityTypes, entityTypeFQN }) => { | ||||
|   const history = useHistory(); | ||||
| 
 | ||||
|   const [activeTab, setActiveTab] = useState<number>(1); | ||||
|   const [selectedEntityType, setSelectedEntityType] = useState<Type>( | ||||
|     {} as Type | ||||
|   ); | ||||
|   const [selectedEntityTypeDetail, setSelectedEntityTypeDetail] = | ||||
|     useState<Type>({} as Type); | ||||
| 
 | ||||
|   const fetchTypeDetail = (typeFQN: string) => { | ||||
|     getTypeByFQN(typeFQN) | ||||
|       .then((res) => { | ||||
|         setSelectedEntityTypeDetail(res); | ||||
|       }) | ||||
|       .catch((err: AxiosError) => showErrorToast(err)); | ||||
|   }; | ||||
| 
 | ||||
|   const onTabChange = (tab: number) => { | ||||
|     setActiveTab(tab); | ||||
|   }; | ||||
| 
 | ||||
|   const onEntityTypeSelect = (entityType: Type) => { | ||||
|     setSelectedEntityType(entityType); | ||||
|   }; | ||||
| 
 | ||||
|   const handleAddProperty = () => { | ||||
|     const path = getAddCustomPropertyPath( | ||||
|       selectedEntityTypeDetail.fullyQualifiedName as string | ||||
|     ); | ||||
|     history.push(path); | ||||
|   }; | ||||
| 
 | ||||
|   const schemaCheck = activeTab === 2 && !isEmpty(selectedEntityTypeDetail); | ||||
|   const schemaValue = selectedEntityTypeDetail.schema || '{}'; | ||||
| 
 | ||||
|   const customPropertiesCheck = | ||||
|     activeTab === 1 && !isEmpty(selectedEntityTypeDetail); | ||||
|   const customProperties = selectedEntityTypeDetail.customProperties || []; | ||||
| 
 | ||||
|   const tabs = [ | ||||
|     { | ||||
|       name: 'Custom Properties', | ||||
|       isProtected: false, | ||||
|       position: 1, | ||||
|       count: customProperties.length, | ||||
|     }, | ||||
|     { | ||||
|       name: 'Schema', | ||||
|       isProtected: false, | ||||
|       position: 2, | ||||
|     }, | ||||
|   ]; | ||||
| 
 | ||||
|   const componentCheck = Boolean(entityTypes.length); | ||||
| 
 | ||||
|   const updateEntityType = (properties: Type['customProperties']) => { | ||||
|     const patch = compare(selectedEntityTypeDetail, { | ||||
|       ...selectedEntityTypeDetail, | ||||
|       customProperties: properties, | ||||
|     }); | ||||
| 
 | ||||
|     updateType(selectedEntityTypeDetail.id as string, patch) | ||||
|       .then((res) => { | ||||
|         const { customProperties: properties } = res; | ||||
| 
 | ||||
|         setSelectedEntityTypeDetail((prev) => ({ | ||||
|           ...prev, | ||||
|           customProperties: properties, | ||||
|         })); | ||||
|       }) | ||||
|       .catch((err: AxiosError) => showErrorToast(err)); | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (entityTypes.length) { | ||||
|       const entityType = | ||||
|         entityTypes.find((type) => type.fullyQualifiedName === entityTypeFQN) || | ||||
|         entityTypes[0]; | ||||
|       onEntityTypeSelect(entityType); | ||||
|     } | ||||
|   }, [entityTypes, entityTypeFQN]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!isEmpty(selectedEntityType)) { | ||||
|       fetchTypeDetail(selectedEntityType.fullyQualifiedName as string); | ||||
|     } | ||||
|   }, [selectedEntityType]); | ||||
| 
 | ||||
|   return ( | ||||
|     <PageContainer> | ||||
|       {componentCheck ? ( | ||||
|         <PageLayout | ||||
|           leftPanel={ | ||||
|             <LeftPanel | ||||
|               selectedType={selectedEntityTypeDetail} | ||||
|               typeList={entityTypes} | ||||
|             /> | ||||
|           }> | ||||
|           <TabsPane | ||||
|             activeTab={activeTab} | ||||
|             setActiveTab={onTabChange} | ||||
|             tabs={tabs} | ||||
|           /> | ||||
|           <div className="tw-mt-4"> | ||||
|             {schemaCheck && ( | ||||
|               <div data-testid="entity-schema"> | ||||
|                 <SchemaEditor | ||||
|                   className="tw-border tw-border-main tw-rounded-md tw-py-4" | ||||
|                   editorClass="custom-entity-schema" | ||||
|                   value={JSON.parse(schemaValue)} | ||||
|                 /> | ||||
|               </div> | ||||
|             )} | ||||
|             {customPropertiesCheck && ( | ||||
|               <div data-testid="entity-custom-fields"> | ||||
|                 <div className="tw-flex tw-justify-end"> | ||||
|                   <Button | ||||
|                     className="tw-mb-4 tw-py-1 tw-px-2 tw-rounded" | ||||
|                     data-testid="add-field-button" | ||||
|                     size="custom" | ||||
|                     theme="primary" | ||||
|                     onClick={() => handleAddProperty()}> | ||||
|                     Add Property | ||||
|                   </Button> | ||||
|                 </div> | ||||
|                 <CustomPropertyTable | ||||
|                   customProperties={customProperties} | ||||
|                   updateEntityType={updateEntityType} | ||||
|                 /> | ||||
|               </div> | ||||
|             )} | ||||
|           </div> | ||||
|         </PageLayout> | ||||
|       ) : ( | ||||
|         <ErrorPlaceHolder> | ||||
|           {jsonData['message']['no-custom-entity']} | ||||
|         </ErrorPlaceHolder> | ||||
|       )} | ||||
|     </PageContainer> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default CustomEntityDetail; | ||||
| @ -60,6 +60,7 @@ const mockProperties = [ | ||||
| ]; | ||||
| 
 | ||||
| const mockProp = { | ||||
|   hasAccess: true, | ||||
|   customProperties: mockProperties, | ||||
|   updateEntityType: mockUpdateEntityType, | ||||
| }; | ||||
|  | ||||
| @ -11,9 +11,11 @@ | ||||
|  *  limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| import { Tooltip } from 'antd'; | ||||
| import classNames from 'classnames'; | ||||
| import { isEmpty, uniqueId } from 'lodash'; | ||||
| import React, { FC, Fragment, useState } from 'react'; | ||||
| import { NO_PERMISSION_FOR_ACTION } from '../../constants/HelperTextUtil'; | ||||
| import { CustomProperty, Type } from '../../generated/entity/type'; | ||||
| import { getEntityName, isEven } from '../../utils/CommonUtils'; | ||||
| import SVGIcons, { Icons } from '../../utils/SvgUtils'; | ||||
| @ -22,6 +24,7 @@ import ConfirmationModal from '../Modals/ConfirmationModal/ConfirmationModal'; | ||||
| import { ModalWithMarkdownEditor } from '../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; | ||||
| 
 | ||||
| interface CustomPropertyTableProp { | ||||
|   hasAccess: boolean; | ||||
|   customProperties: CustomProperty[]; | ||||
|   updateEntityType: (customProperties: Type['customProperties']) => void; | ||||
| } | ||||
| @ -31,6 +34,7 @@ type Operation = 'delete' | 'update' | 'no-operation'; | ||||
| export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({ | ||||
|   customProperties, | ||||
|   updateEntityType, | ||||
|   hasAccess, | ||||
| }) => { | ||||
|   const [selectedProperty, setSelectedProperty] = useState<CustomProperty>( | ||||
|     {} as CustomProperty | ||||
| @ -120,34 +124,42 @@ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({ | ||||
|                   </td> | ||||
|                   <td className="tableBody-cell"> | ||||
|                     <div className="tw-flex"> | ||||
|                       <button | ||||
|                         className="tw-cursor-pointer" | ||||
|                         data-testid="edit-button" | ||||
|                         onClick={() => { | ||||
|                           setSelectedProperty(property); | ||||
|                           setOperation('update'); | ||||
|                         }}> | ||||
|                         <SVGIcons | ||||
|                           alt="edit" | ||||
|                           icon={Icons.EDIT} | ||||
|                           title="Edit" | ||||
|                           width="16px" | ||||
|                         /> | ||||
|                       </button> | ||||
|                       <button | ||||
|                         className="tw-cursor-pointer tw-ml-4" | ||||
|                         data-testid="delete-button" | ||||
|                         onClick={() => { | ||||
|                           setSelectedProperty(property); | ||||
|                           setOperation('delete'); | ||||
|                         }}> | ||||
|                         <SVGIcons | ||||
|                           alt="delete" | ||||
|                           icon={Icons.DELETE} | ||||
|                           title="Delete" | ||||
|                           width="16px" | ||||
|                         /> | ||||
|                       </button> | ||||
|                       <Tooltip | ||||
|                         title={hasAccess ? 'Edit' : NO_PERMISSION_FOR_ACTION}> | ||||
|                         <button | ||||
|                           className="tw-cursor-pointer" | ||||
|                           data-testid="edit-button" | ||||
|                           disabled={!hasAccess} | ||||
|                           onClick={() => { | ||||
|                             setSelectedProperty(property); | ||||
|                             setOperation('update'); | ||||
|                           }}> | ||||
|                           <SVGIcons | ||||
|                             alt="edit" | ||||
|                             icon={Icons.EDIT} | ||||
|                             title="Edit" | ||||
|                             width="16px" | ||||
|                           /> | ||||
|                         </button> | ||||
|                       </Tooltip> | ||||
|                       <Tooltip | ||||
|                         title={hasAccess ? 'Delete' : NO_PERMISSION_FOR_ACTION}> | ||||
|                         <button | ||||
|                           className="tw-cursor-pointer tw-ml-4" | ||||
|                           data-testid="delete-button" | ||||
|                           disabled={!hasAccess} | ||||
|                           onClick={() => { | ||||
|                             setSelectedProperty(property); | ||||
|                             setOperation('delete'); | ||||
|                           }}> | ||||
|                           <SVGIcons | ||||
|                             alt="delete" | ||||
|                             icon={Icons.DELETE} | ||||
|                             title="Delete" | ||||
|                             width="16px" | ||||
|                           /> | ||||
|                         </button> | ||||
|                       </Tooltip> | ||||
|                     </div> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|  | ||||
| @ -11,10 +11,10 @@ | ||||
|  *  limitations under the License. | ||||
|  */ | ||||
| 
 | ||||
| import { Col, Empty, Row } from 'antd'; | ||||
| import { Col, Empty, Row, Tooltip } from 'antd'; | ||||
| import { AxiosError } from 'axios'; | ||||
| import { compare } from 'fast-json-patch'; | ||||
| import { isEmpty, isUndefined } from 'lodash'; | ||||
| import { isUndefined } from 'lodash'; | ||||
| import React, { useEffect, useMemo, useState } from 'react'; | ||||
| import { useHistory, useParams } from 'react-router-dom'; | ||||
| import { getTypeByFQN, updateType } from '../../axiosAPIs/metadataTypeAPI'; | ||||
| @ -24,15 +24,20 @@ import TabsPane from '../../components/common/TabsPane/TabsPane'; | ||||
| import { CustomPropertyTable } from '../../components/CustomEntityDetail/CustomPropertyTable'; | ||||
| import Loader from '../../components/Loader/Loader'; | ||||
| import { usePermissionProvider } from '../../components/PermissionProvider/PermissionProvider'; | ||||
| import { | ||||
|   OperationPermission, | ||||
|   ResourceEntity, | ||||
| } from '../../components/PermissionProvider/PermissionProvider.interface'; | ||||
| import SchemaEditor from '../../components/schema-editor/SchemaEditor'; | ||||
| import { getAddCustomPropertyPath } from '../../constants/constants'; | ||||
| import { customAttributesPath } from '../../constants/globalSettings.constants'; | ||||
| import { NO_PERMISSION_TO_VIEW } from '../../constants/HelperTextUtil'; | ||||
| import { Operation } from '../../generated/entity/policies/policy'; | ||||
| import { | ||||
|   NO_PERMISSION_FOR_ACTION, | ||||
|   NO_PERMISSION_TO_VIEW, | ||||
| } from '../../constants/HelperTextUtil'; | ||||
| import { Type } from '../../generated/entity/type'; | ||||
| import jsonData from '../../jsons/en'; | ||||
| import { getResourceEntityFromCustomProperty } from '../../utils/CustomPropertyUtils'; | ||||
| import { checkPermission } from '../../utils/PermissionsUtils'; | ||||
| import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; | ||||
| import { showErrorToast } from '../../utils/ToastUtils'; | ||||
| import './CustomPropertiesPageV1.less'; | ||||
| 
 | ||||
| @ -49,18 +54,32 @@ const CustomEntityDetailV1 = () => { | ||||
|   const tabAttributePath = | ||||
|     customAttributesPath[tab as keyof typeof customAttributesPath]; | ||||
| 
 | ||||
|   const { permissions } = usePermissionProvider(); | ||||
|   const { getEntityPermission } = usePermissionProvider(); | ||||
| 
 | ||||
|   const viewPermission = useMemo(() => { | ||||
|     return ( | ||||
|       !isEmpty(permissions) && | ||||
|       checkPermission( | ||||
|         Operation.ViewAll, | ||||
|         getResourceEntityFromCustomProperty(tab), | ||||
|         permissions | ||||
|       ) | ||||
|     ); | ||||
|   }, [permissions, tab]); | ||||
|   const [propertyPermission, setPropertyPermission] = | ||||
|     useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION); | ||||
| 
 | ||||
|   const fetchPermission = async () => { | ||||
|     try { | ||||
|       const response = await getEntityPermission( | ||||
|         ResourceEntity.TYPE, | ||||
|         selectedEntityTypeDetail.id as string | ||||
|       ); | ||||
|       setPropertyPermission(response); | ||||
|     } catch (error) { | ||||
|       showErrorToast(error as AxiosError); | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   const viewPermission = useMemo( | ||||
|     () => propertyPermission.ViewAll, | ||||
|     [propertyPermission, tab] | ||||
|   ); | ||||
| 
 | ||||
|   const editPermission = useMemo( | ||||
|     () => propertyPermission.EditAll, | ||||
|     [propertyPermission, tab] | ||||
|   ); | ||||
| 
 | ||||
|   const fetchTypeDetail = async (typeFQN: string) => { | ||||
|     setIsLoading(true); | ||||
| @ -126,6 +145,12 @@ const CustomEntityDetailV1 = () => { | ||||
|     } | ||||
|   }, [tab]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (selectedEntityTypeDetail?.id) { | ||||
|       fetchPermission(); | ||||
|     } | ||||
|   }, [selectedEntityTypeDetail]); | ||||
| 
 | ||||
|   if (isLoading) { | ||||
|     return <Loader />; | ||||
|   } | ||||
| @ -163,17 +188,22 @@ const CustomEntityDetailV1 = () => { | ||||
|         {activeTab === 1 && ( | ||||
|           <div data-testid="entity-custom-fields"> | ||||
|             <div className="tw-flex tw-justify-end"> | ||||
|               <Button | ||||
|                 className="tw-mb-4 tw-py-1 tw-px-2 tw-rounded" | ||||
|                 data-testid="add-field-button" | ||||
|                 size="custom" | ||||
|                 theme="primary" | ||||
|                 onClick={() => handleAddProperty()}> | ||||
|                 Add Property | ||||
|               </Button> | ||||
|               <Tooltip | ||||
|                 title={editPermission ? 'Add' : NO_PERMISSION_FOR_ACTION}> | ||||
|                 <Button | ||||
|                   className="tw-mb-4 tw-py-1 tw-px-2 tw-rounded" | ||||
|                   data-testid="add-field-button" | ||||
|                   disabled={!editPermission} | ||||
|                   size="custom" | ||||
|                   theme="primary" | ||||
|                   onClick={() => handleAddProperty()}> | ||||
|                   Add Property | ||||
|                 </Button> | ||||
|               </Tooltip> | ||||
|             </div> | ||||
|             <CustomPropertyTable | ||||
|               customProperties={selectedEntityTypeDetail.customProperties || []} | ||||
|               hasAccess={editPermission} | ||||
|               updateEntityType={updateEntityType} | ||||
|             /> | ||||
|           </div> | ||||
|  | ||||
| @ -466,14 +466,9 @@ const AuthenticatedAppRouter: FunctionComponent = () => { | ||||
|         )} | ||||
|         path={ROUTES.BOTS_PROFILE} | ||||
|       /> | ||||
|       <AdminProtectedRoute | ||||
|       <Route | ||||
|         exact | ||||
|         component={AddCustomProperty} | ||||
|         hasPermission={checkPermission( | ||||
|           Operation.Create, | ||||
|           ResourceEntity.TYPE, | ||||
|           permissions | ||||
|         )} | ||||
|         path={ROUTES.ADD_CUSTOM_PROPERTY} | ||||
|       /> | ||||
|       <Route | ||||
|  | ||||
| @ -267,7 +267,7 @@ const GlobalSettingRouter = () => { | ||||
|         component={CustomPropertiesPageV1} | ||||
|         hasPermission={checkPermission( | ||||
|           Operation.ViewAll, | ||||
|           ResourceEntity.ALL, | ||||
|           ResourceEntity.TYPE, | ||||
|           permissions | ||||
|         )} | ||||
|         path={getSettingCategoryPath( | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sachin Chaurasiya
						Sachin Chaurasiya