diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomEntityDetail.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomEntityDetail.test.tsx
deleted file mode 100644
index e6c642f7627..00000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomEntityDetail.test.tsx
+++ /dev/null
@@ -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 }) => (
-
{children}
- ));
-});
-
-jest.mock('../../constants/constants', () => ({
- getAddCustomPropertyPath: jest.fn().mockReturnValue('/custom-entity/table'),
-}));
-
-jest.mock('../common/TabsPane/TabsPane', () =>
- jest.fn().mockImplementation(({ setActiveTab, tabs }) => {
- return (
-
-
- {tabs.map((tab: Tab) => (
- setActiveTab?.(tab.position)}>
- {tab.name}
-
- ))}
-
-
- );
- })
-);
-
-jest.mock('../schema-editor/SchemaEditor', () =>
- jest
- .fn()
- .mockReturnValue(Schema Editor
)
-);
-
-jest.mock('./CustomPropertyTable', () => ({
- CustomPropertyTable: jest
- .fn()
- .mockReturnValue(
- CustomPropertyTable
- ),
-}));
-
-jest.mock('./LeftPanel', () => ({
- LeftPanel: jest
- .fn()
- .mockReturnValue(LeftPanel
),
-}));
-
-describe('Test Custom Entity Detail Component', () => {
- it('Should render custom entity component', async () => {
- const { findByTestId } = render(
- ,
- {
- 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(
- ,
- {
- 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(
- ,
- {
- 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();
- });
-});
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomEntityDetail.tsx b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomEntityDetail.tsx
deleted file mode 100644
index 3ee1b460b71..00000000000
--- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomEntityDetail.tsx
+++ /dev/null
@@ -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;
- entityTypeFQN?: string;
-}
-
-const CustomEntityDetail: FC = ({ entityTypes, entityTypeFQN }) => {
- const history = useHistory();
-
- const [activeTab, setActiveTab] = useState(1);
- const [selectedEntityType, setSelectedEntityType] = useState(
- {} as Type
- );
- const [selectedEntityTypeDetail, setSelectedEntityTypeDetail] =
- useState({} 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 (
-
- {componentCheck ? (
-
- }>
-
-
- {schemaCheck && (
-
-
-
- )}
- {customPropertiesCheck && (
-
-
- handleAddProperty()}>
- Add Property
-
-
-
-
- )}
-
-
- ) : (
-
- {jsonData['message']['no-custom-entity']}
-
- )}
-
- );
-};
-
-export default CustomEntityDetail;
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx
index f55743a8604..f087cd22a45 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.test.tsx
@@ -60,6 +60,7 @@ const mockProperties = [
];
const mockProp = {
+ hasAccess: true,
customProperties: mockProperties,
updateEntityType: mockUpdateEntityType,
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx
index b696141acc0..56f5416fd04 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/CustomEntityDetail/CustomPropertyTable.tsx
@@ -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 = ({
customProperties,
updateEntityType,
+ hasAccess,
}) => {
const [selectedProperty, setSelectedProperty] = useState(
{} as CustomProperty
@@ -120,34 +124,42 @@ export const CustomPropertyTable: FC = ({
- {
- setSelectedProperty(property);
- setOperation('update');
- }}>
-
-
- {
- setSelectedProperty(property);
- setOperation('delete');
- }}>
-
-
+
+ {
+ setSelectedProperty(property);
+ setOperation('update');
+ }}>
+
+
+
+
+ {
+ setSelectedProperty(property);
+ setOperation('delete');
+ }}>
+
+
+
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx
index 4d8e93a8c95..46d6373cec5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/CustomPropertiesPageV1/CustomPropertiesPageV1.tsx
@@ -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(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 ;
}
@@ -163,17 +188,22 @@ const CustomEntityDetailV1 = () => {
{activeTab === 1 && (
- handleAddProperty()}>
- Add Property
-
+
+ handleAddProperty()}>
+ Add Property
+
+
diff --git a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx
index 5d6d6621a46..35af00124e9 100644
--- a/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/router/AuthenticatedAppRouter.tsx
@@ -466,14 +466,9 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
)}
path={ROUTES.BOTS_PROFILE}
/>
-
{
component={CustomPropertiesPageV1}
hasPermission={checkPermission(
Operation.ViewAll,
- ResourceEntity.ALL,
+ ResourceEntity.TYPE,
permissions
)}
path={getSettingCategoryPath(