UI : Add custom properties support for all entities (#6350)

*  UI : Add custom properties support for all entities

* Add custom properties for topic entity

* use async await pattern

* Add custom property support for dashboard entity

* Add custom property support for pipeline entity

* minor fix

* Fix unit test

* Add custom property support for ml model entity

* Fix unit test

* Add unit tests

* Add extension

* Fix formatting

* Addressing review comment

* Addressing review comments
This commit is contained in:
Sachin Chaurasiya 2022-07-27 20:59:28 +05:30 committed by GitHub
parent c6efe8c142
commit b9eaeb8aeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 341 additions and 39 deletions

View File

@ -53,6 +53,8 @@ import { getTagsWithoutTier } from '../../utils/TableUtils';
import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropertyTable';
import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
@ -112,6 +114,7 @@ const DashboardDetails = ({
fetchFeedHandler,
updateThreadHandler,
entityFieldTaskCount,
onExtensionUpdate,
}: DashboardDetailsProps) => {
const [isEdit, setIsEdit] = useState(false);
const [followersCount, setFollowersCount] = useState(0);
@ -192,6 +195,11 @@ const DashboardDetails = ({
isProtected: false,
position: 3,
},
{
name: 'Custom Properties',
isProtected: false,
position: 4,
},
{
name: 'Manage',
icon: {
@ -202,7 +210,7 @@ const DashboardDetails = ({
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 4,
position: 5,
},
];
@ -739,6 +747,15 @@ const DashboardDetails = ({
</div>
)}
{activeTab === 4 && (
<CustomPropertyTable
entityDetails={
dashboardDetails as CustomPropertyProps['entityDetails']
}
entityType={EntityType.DASHBOARD}
handleExtentionUpdate={onExtensionUpdate}
/>
)}
{activeTab === 5 && (
<div>
<ManageTabComponent
allowDelete

View File

@ -92,4 +92,5 @@ export interface DashboardDetailsProps {
postFeedHandler: (value: string, id: string) => void;
deletePostHandler: (threadId: string, postId: string) => void;
updateThreadHandler: ThreadUpdatedFunc;
onExtensionUpdate: (updatedDashboard: Dashboard) => void;
}

View File

@ -118,6 +118,7 @@ const DashboardDetailsProps = {
paging: {} as Paging,
fetchFeedHandler: jest.fn(),
updateThreadHandler: jest.fn(),
onExtensionUpdate: jest.fn(),
};
const mockObserve = jest.fn();
@ -336,7 +337,7 @@ describe('Test DashboardDetails component', () => {
it('Check if active tab is manage', async () => {
const { container } = render(
<DashboardDetails {...DashboardDetailsProps} activeTab={4} />,
<DashboardDetails {...DashboardDetailsProps} activeTab={5} />,
{
wrapper: MemoryRouter,
}
@ -346,6 +347,21 @@ describe('Test DashboardDetails component', () => {
expect(manage).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => {
const { container } = render(
<DashboardDetails {...DashboardDetailsProps} activeTab={4} />,
{
wrapper: MemoryRouter,
}
);
const customProperties = await findByTestId(
container,
'custom-properties-table'
);
expect(customProperties).toBeInTheDocument();
});
it('Should create an observer if IntersectionObserver is available', async () => {
const { container } = render(
<DashboardDetails {...DashboardDetailsProps} activeTab={4} />,

View File

@ -57,6 +57,8 @@ import {
} from '../../utils/TableUtils';
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropertyTable';
import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import TabsPane from '../common/TabsPane/TabsPane';
@ -75,7 +77,6 @@ import SchemaTab from '../SchemaTab/SchemaTab.component';
import TableProfiler from '../TableProfiler/TableProfiler.component';
import TableProfilerGraph from '../TableProfiler/TableProfilerGraph.component';
import TableQueries from '../TableQueries/TableQueries';
import { CustomPropertyTable } from './CustomPropertyTable/CustomPropertyTable';
import { DatasetDetailsProps } from './DatasetDetails.interface';
const DatasetDetails: React.FC<DatasetDetailsProps> = ({
@ -295,12 +296,6 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
},
{
name: 'Custom Properties',
icon: {
alt: 'custom_properties',
name: 'custom_properties-light-grey',
title: 'custom_properties',
selectedName: 'custom_properties-primery',
},
isProtected: false,
position: 9,
},
@ -842,8 +837,11 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
)}
{activeTab === 9 && (
<CustomPropertyTable
entityDetails={
tableDetails as CustomPropertyProps['entityDetails']
}
entityType={EntityType.TABLE}
handleExtentionUpdate={handleExtentionUpdate}
tableDetails={tableDetails}
/>
)}
{activeTab === 10 && (

View File

@ -350,6 +350,21 @@ describe('Test MyDataDetailsPage page', () => {
expect(manage).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => {
const { container } = render(
<DatasetDetails {...DatasetDetailsProps} activeTab={9} />,
{
wrapper: MemoryRouter,
}
);
const customProperties = await findByTestId(
container,
'custom-properties-table'
);
expect(customProperties).toBeInTheDocument();
});
it('Should create an observer if IntersectionObserver is available', async () => {
const { container } = render(
<DatasetDetails {...DatasetDetailsProps} activeTab={9} />,

View File

@ -152,6 +152,7 @@ const mockProp = {
lineageLeafNodes: {} as LeafNodes,
isNodeLoading: { id: undefined, state: false },
},
onExtensionUpdate: jest.fn(),
};
jest.mock('../ManageTab/ManageTab.component', () => {
@ -254,7 +255,7 @@ describe('Test MlModel entity detail component', () => {
it('Should render manage component for manage tab', async () => {
const { container } = render(
<MlModelDetailComponent {...mockProp} activeTab={4} />,
<MlModelDetailComponent {...mockProp} activeTab={5} />,
{
wrapper: MemoryRouter,
}
@ -266,4 +267,19 @@ describe('Test MlModel entity detail component', () => {
expect(detailContainer).toBeInTheDocument();
expect(manageTab).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => {
const { container } = render(
<MlModelDetailComponent {...mockProp} activeTab={4} />,
{
wrapper: MemoryRouter,
}
);
const customProperties = await findByTestId(
container,
'custom-properties-table'
);
expect(customProperties).toBeInTheDocument();
});
});

View File

@ -45,6 +45,8 @@ import { LabelType, State, TagLabel } from '../../generated/type/tagLabel';
import { getEntityName, getEntityPlaceHolder } from '../../utils/CommonUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getTagsWithoutTier, getTierTags } from '../../utils/TableUtils';
import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropertyTable';
import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import TabsPane from '../common/TabsPane/TabsPane';
@ -75,6 +77,7 @@ interface MlModelDetailProp extends HTMLAttributes<HTMLDivElement> {
lineageLeafNodes: LeafNodes;
isNodeLoading: LoadingNodeState;
};
onExtensionUpdate: (updatedMlModel: Mlmodel) => void;
}
const MlModelDetail: FC<MlModelDetailProp> = ({
@ -88,6 +91,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
settingsUpdateHandler,
updateMlModelFeatures,
lineageTabData,
onExtensionUpdate,
}) => {
const [followersCount, setFollowersCount] = useState<number>(0);
const [isFollowing, setIsFollowing] = useState<boolean>(false);
@ -213,6 +217,11 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
isProtected: false,
position: 3,
},
{
name: 'Custom Properties',
isProtected: false,
position: 4,
},
{
name: 'Manage',
icon: {
@ -223,7 +232,7 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
},
isProtected: false,
protectedState: !mlModelDetail.owner || hasEditAccess(),
position: 4,
position: 5,
},
];
@ -536,6 +545,15 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
</div>
)}
{activeTab === 4 && (
<CustomPropertyTable
entityDetails={
mlModelDetail as CustomPropertyProps['entityDetails']
}
entityType={EntityType.MLMODEL}
handleExtentionUpdate={onExtensionUpdate}
/>
)}
{activeTab === 5 && (
<div>
<ManageTabComponent
allowDelete

View File

@ -43,6 +43,8 @@ import { getEntityFieldThreadCounts } from '../../utils/FeedUtils';
import { getTagsWithoutTier } from '../../utils/TableUtils';
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropertyTable';
import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import TabsPane from '../common/TabsPane/TabsPane';
@ -100,6 +102,7 @@ const PipelineDetails = ({
pipelineStatus,
updateThreadHandler,
entityFieldTaskCount,
onExtensionUpdate,
}: PipeLineDetailsProp) => {
const [isEdit, setIsEdit] = useState(false);
const [followersCount, setFollowersCount] = useState(0);
@ -186,6 +189,11 @@ const PipelineDetails = ({
isProtected: false,
position: 3,
},
{
name: 'Custom Properties',
isProtected: false,
position: 4,
},
{
name: 'Manage',
icon: {
@ -196,7 +204,7 @@ const PipelineDetails = ({
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 4,
position: 5,
},
];
@ -524,6 +532,15 @@ const PipelineDetails = ({
</div>
)}
{activeTab === 4 && (
<CustomPropertyTable
entityDetails={
pipelineDetails as CustomPropertyProps['entityDetails']
}
entityType={EntityType.PIPELINE}
handleExtentionUpdate={onExtensionUpdate}
/>
)}
{activeTab === 5 && (
<div>
<ManageTabComponent
allowDelete

View File

@ -79,4 +79,5 @@ export interface PipeLineDetailsProp {
postFeedHandler: (value: string, id: string) => void;
deletePostHandler: (threadId: string, postId: string) => void;
updateThreadHandler: ThreadUpdatedFunc;
onExtensionUpdate: (updatedPipeline: Pipeline) => void;
}

View File

@ -125,6 +125,7 @@ const PipelineDetailsProps = {
pipelineStatus: [],
isPipelineStatusLoading: false,
updateThreadHandler: jest.fn(),
onExtensionUpdate: jest.fn(),
};
const mockObserve = jest.fn();
@ -274,7 +275,7 @@ describe('Test PipelineDetails component', () => {
it('Check if active tab is manage', async () => {
const { container } = render(
<PipelineDetails {...PipelineDetailsProps} activeTab={4} />,
<PipelineDetails {...PipelineDetailsProps} activeTab={5} />,
{
wrapper: MemoryRouter,
}
@ -284,6 +285,21 @@ describe('Test PipelineDetails component', () => {
expect(manage).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => {
const { container } = render(
<PipelineDetails {...PipelineDetailsProps} activeTab={4} />,
{
wrapper: MemoryRouter,
}
);
const customProperties = await findByTestId(
container,
'custom-properties-table'
);
expect(customProperties).toBeInTheDocument();
});
it('Should create an observer if IntersectionObserver is available', async () => {
const { container } = render(
<PipelineDetails {...PipelineDetailsProps} activeTab={4} />,

View File

@ -44,6 +44,8 @@ import { bytesToSize } from '../../utils/StringsUtils';
import { getTagsWithoutTier } from '../../utils/TableUtils';
import ActivityFeedList from '../ActivityFeed/ActivityFeedList/ActivityFeedList';
import ActivityThreadPanel from '../ActivityFeed/ActivityThreadPanel/ActivityThreadPanel';
import { CustomPropertyTable } from '../common/CustomPropertyTable/CustomPropertyTable';
import { CustomPropertyProps } from '../common/CustomPropertyTable/CustomPropertyTable.interface';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import TabsPane from '../common/TabsPane/TabsPane';
@ -97,6 +99,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
updateThreadHandler,
entityFieldTaskCount,
lineageTabData,
onExtensionUpdate,
}: TopicDetailsProps) => {
const [isEdit, setIsEdit] = useState(false);
const [followersCount, setFollowersCount] = useState(0);
@ -221,6 +224,11 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
isProtected: false,
position: 5,
},
{
name: 'Custom Properties',
isProtected: false,
position: 6,
},
{
name: 'Manage',
icon: {
@ -231,7 +239,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
},
isProtected: true,
protectedState: !owner || hasEditAccess(),
position: 6,
position: 7,
},
];
const extraInfo: Array<ExtraInfo> = [
@ -557,6 +565,15 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
</div>
)}
{activeTab === 6 && (
<CustomPropertyTable
entityDetails={
topicDetails as CustomPropertyProps['entityDetails']
}
entityType={EntityType.TOPIC}
handleExtentionUpdate={onExtensionUpdate}
/>
)}
{activeTab === 7 && (
<div>
<ManageTabComponent
allowDelete

View File

@ -84,4 +84,5 @@ export interface TopicDetailsProps {
lineageLeafNodes: LeafNodes;
isNodeLoading: LoadingNodeState;
};
onExtensionUpdate: (updatedTopic: Topic) => void;
}

View File

@ -111,6 +111,7 @@ const TopicDetailsProps = {
lineageLeafNodes: {} as LeafNodes,
isNodeLoading: { id: undefined, state: false },
},
onExtensionUpdate: jest.fn(),
};
const mockObserve = jest.fn();
@ -240,7 +241,7 @@ describe('Test TopicDetails component', () => {
it('Check if active tab is manage', async () => {
const { container } = render(
<TopicDetails {...TopicDetailsProps} activeTab={6} />,
<TopicDetails {...TopicDetailsProps} activeTab={7} />,
{
wrapper: MemoryRouter,
}
@ -263,6 +264,21 @@ describe('Test TopicDetails component', () => {
expect(detailContainer).toBeInTheDocument();
});
it('Check if active tab is custom properties', async () => {
const { container } = render(
<TopicDetails {...TopicDetailsProps} activeTab={6} />,
{
wrapper: MemoryRouter,
}
);
const customProperties = await findByTestId(
container,
'custom-properties-table'
);
expect(customProperties).toBeInTheDocument();
});
it('Should create an observer if IntersectionObserver is available', async () => {
const { container } = render(
<TopicDetails {...TopicDetailsProps} activeTab={4} />,

View File

@ -0,0 +1,27 @@
/*
* 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 { EntityType } from '../../../enums/entity.enum';
import { Dashboard } from '../../../generated/entity/data/dashboard';
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
import { Pipeline } from '../../../generated/entity/data/pipeline';
import { Table } from '../../../generated/entity/data/table';
import { Topic } from '../../../generated/entity/data/topic';
export type EntityDetails = Table & Topic & Dashboard & Pipeline & Mlmodel;
export interface CustomPropertyProps {
entityDetails: EntityDetails;
entityType: EntityType;
handleExtentionUpdate: (updatedTable: EntityDetails) => void;
}

View File

@ -14,7 +14,12 @@
import { render } from '@testing-library/react';
import React from 'react';
import { getTypeByFQN } from '../../../axiosAPIs/metadataTypeAPI';
import { EntityType } from '../../../enums/entity.enum';
import { Dashboard } from '../../../generated/entity/data/dashboard';
import { Mlmodel } from '../../../generated/entity/data/mlmodel';
import { Pipeline } from '../../../generated/entity/data/pipeline';
import { Table } from '../../../generated/entity/data/table';
import { Topic } from '../../../generated/entity/data/topic';
import { CustomPropertyTable } from './CustomPropertyTable';
const mockCustomProperties = [
@ -53,12 +58,13 @@ jest.mock('../../../axiosAPIs/metadataTypeAPI', () => ({
),
}));
const mockTableDetails = {} as Table;
const mockTableDetails = {} as Table & Topic & Dashboard & Pipeline & Mlmodel;
const handleExtentionUpdate = jest.fn();
const mockProp = {
tableDetails: mockTableDetails,
entityDetails: mockTableDetails,
handleExtentionUpdate,
entityType: EntityType.TABLE,
};
describe('Test CustomProperty Table Component', () => {

View File

@ -16,25 +16,21 @@ import classNames from 'classnames';
import { uniqueId } from 'lodash';
import React, { FC, useEffect, useState } from 'react';
import { getTypeByFQN } from '../../../axiosAPIs/metadataTypeAPI';
import { Table } from '../../../generated/entity/data/table';
import { Type } from '../../../generated/entity/type';
import { isEven } from '../../../utils/CommonUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import { CustomPropertyProps } from './CustomPropertyTable.interface';
import { PropertyValue } from './PropertyValue';
interface Props {
tableDetails: Table;
handleExtentionUpdate: (updatedTable: Table) => void;
}
export const CustomPropertyTable: FC<Props> = ({
tableDetails,
export const CustomPropertyTable: FC<CustomPropertyProps> = ({
entityDetails,
handleExtentionUpdate,
entityType,
}) => {
const [entityTypeDetail, setEntityTypeDetail] = useState<Type>({} as Type);
const fetchTypeDetail = () => {
getTypeByFQN('table')
getTypeByFQN(entityType)
.then((res: AxiosResponse) => {
setEntityTypeDetail(res.data);
})
@ -43,10 +39,12 @@ export const CustomPropertyTable: FC<Props> = ({
const customProperties = entityTypeDetail.customProperties || [];
const extension = tableDetails.extension;
const extension = entityDetails.extension;
const onExtensionUpdate = (updatedExtension: Table['extension']) => {
handleExtentionUpdate({ ...tableDetails, extension: updatedExtension });
const onExtensionUpdate = (
updatedExtension: CustomPropertyProps['entityDetails']['extension']
) => {
handleExtentionUpdate({ ...entityDetails, extension: updatedExtension });
};
useEffect(() => {

View File

@ -16,8 +16,8 @@ import React, { FC, Fragment, useState } from 'react';
import { Table } from '../../../generated/entity/data/table';
import { EntityReference } from '../../../generated/type/entityReference';
import SVGIcons, { Icons } from '../../../utils/SvgUtils';
import RichTextEditorPreviewer from '../../common/rich-text-editor/RichTextEditorPreviewer';
import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer';
import { PropertyInput } from './PropertyInput';
interface Props {

View File

@ -746,6 +746,27 @@ const DashboardDetailsPage = () => {
updateThreadData(threadId, postId, isThread, data, setEntityThread);
};
const handleExtentionUpdate = async (updatedDashboard: Dashboard) => {
try {
const { data } = await saveUpdatedDashboardData(updatedDashboard);
if (data) {
const { version, owner: ownerValue, tags } = data;
setCurrentVersion(version);
setDashboardDetails(data);
setOwner(ownerValue);
setTier(getTierTags(tags));
} else {
throw jsonData['api-error-messages']['update-entity-error'];
}
} catch (error) {
showErrorToast(
error as AxiosError,
jsonData['api-error-messages']['update-entity-error']
);
}
};
useEffect(() => {
fetchTabSpecificData(dashboardDetailsTabs[activeTab - 1].field);
}, [activeTab]);
@ -816,6 +837,7 @@ const DashboardDetailsPage = () => {
updateThreadHandler={updateThreadHandler}
version={currentVersion as string}
versionHandler={versionHandler}
onExtensionUpdate={handleExtentionUpdate}
/>
)}
</>

View File

@ -356,6 +356,23 @@ const MlModelPage = () => {
});
};
const handleExtentionUpdate = async (updatedMlModel: Mlmodel) => {
try {
const { data } = await saveUpdatedMlModelData(updatedMlModel);
if (data) {
setMlModelDetail(data);
} else {
throw jsonData['api-error-messages']['update-entity-error'];
}
} catch (error) {
showErrorToast(
error as AxiosError,
jsonData['api-error-messages']['update-entity-error']
);
}
};
const getMlModelDetail = () => {
if (!isNil(mlModelDetail) && !isEmpty(mlModelDetail)) {
return (
@ -379,6 +396,7 @@ const MlModelPage = () => {
tagUpdateHandler={onTagUpdate}
unfollowMlModelHandler={unfollowMlModel}
updateMlModelFeatures={updateMlModelFeatures}
onExtensionUpdate={handleExtentionUpdate}
/>
);
} else {

View File

@ -687,6 +687,27 @@ const PipelineDetailsPage = () => {
updateThreadData(threadId, postId, isThread, data, setEntityThread);
};
const handleExtentionUpdate = async (updatedPipeline: Pipeline) => {
try {
const { data } = await saveUpdatedPipelineData(updatedPipeline);
if (data) {
const { version, owner: ownerValue, tags } = data;
setCurrentVersion(version);
setPipelineDetails(data);
setOwner(ownerValue);
setTier(getTierTags(tags));
} else {
throw jsonData['api-error-messages']['update-entity-error'];
}
} catch (error) {
showErrorToast(
error as AxiosError,
jsonData['api-error-messages']['update-entity-error']
);
}
};
useEffect(() => {
fetchTabSpecificData(pipelineDetailsTabs[activeTab - 1].field);
}, [activeTab]);
@ -757,6 +778,7 @@ const PipelineDetailsPage = () => {
updateThreadHandler={updateThreadHandler}
version={currentVersion as string}
versionHandler={versionHandler}
onExtensionUpdate={handleExtentionUpdate}
/>
)}
</>

View File

@ -359,7 +359,12 @@ const TopicDetailsPage: FunctionComponent = () => {
const fetchTopicDetail = (topicFQN: string) => {
setLoading(true);
getTopicByFqn(topicFQN, ['owner', 'followers', 'tags'])
getTopicByFqn(topicFQN, [
TabSpecificField.OWNER,
TabSpecificField.FOLLOWERS,
TabSpecificField.TAGS,
TabSpecificField.EXTENSION,
])
.then((res: AxiosResponse) => {
if (res.data) {
const {
@ -668,6 +673,27 @@ const TopicDetailsPage: FunctionComponent = () => {
updateThreadData(threadId, postId, isThread, data, setEntityThread);
};
const handleExtentionUpdate = async (updatedTopic: Topic) => {
try {
const { data } = await saveUpdatedTopicData(updatedTopic);
if (data) {
const { version, owner: ownerValue, tags } = data;
setCurrentVersion(version);
setTopicDetails(data);
setOwner(ownerValue);
setTier(getTierTags(tags));
} else {
throw jsonData['api-error-messages']['update-entity-error'];
}
} catch (error) {
showErrorToast(
error as AxiosError,
jsonData['api-error-messages']['update-entity-error']
);
}
};
useEffect(() => {
if (topicDetailsTabs[activeTab - 1].path !== tab) {
setActiveTab(getCurrentTopicTab(tab));
@ -743,6 +769,7 @@ const TopicDetailsPage: FunctionComponent = () => {
updateThreadHandler={updateThreadHandler}
version={currentVersion}
versionHandler={versionHandler}
onExtensionUpdate={handleExtentionUpdate}
/>
)}
</>

View File

@ -14,7 +14,7 @@
import { TabSpecificField } from '../enums/entity.enum';
export const defaultFields = `${TabSpecificField.OWNER}, ${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS},
${TabSpecificField.USAGE_SUMMARY}, ${TabSpecificField.CHARTS}`;
${TabSpecificField.USAGE_SUMMARY}, ${TabSpecificField.CHARTS},${TabSpecificField.EXTENSION}`;
export const dashboardDetailsTabs = [
{
@ -31,6 +31,10 @@ export const dashboardDetailsTabs = [
path: 'lineage',
field: TabSpecificField.LINEAGE,
},
{
name: 'Custom Properties',
path: 'custom_properties',
},
{
name: 'Manage',
path: 'manage',
@ -50,11 +54,16 @@ export const getCurrentDashboardTab = (tab: string) => {
break;
case 'manage':
case 'custom_properties':
currentTab = 4;
break;
case 'manage':
currentTab = 5;
break;
case 'details':
default:
currentTab = 1;

View File

@ -14,7 +14,7 @@
import { TabSpecificField } from '../enums/entity.enum';
export const defaultFields = `${TabSpecificField.USAGE_SUMMARY},
${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}, ${TabSpecificField.OWNER}, ${TabSpecificField.DASHBOARD} `;
${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}, ${TabSpecificField.OWNER}, ${TabSpecificField.DASHBOARD} ,${TabSpecificField.EXTENSION}`;
export const mlModelTabs = [
{
@ -30,6 +30,10 @@ export const mlModelTabs = [
path: 'lineage',
field: TabSpecificField.LINEAGE,
},
{
name: 'Custom Properties',
path: 'custom_properties',
},
{
name: 'Manage',
path: 'manage',
@ -47,9 +51,13 @@ export const getCurrentMlModelTab = (tab: string) => {
currentTab = 3;
break;
case 'manage':
case 'custom_properties':
currentTab = 4;
break;
case 'manage':
currentTab = 5;
break;
case 'features':
currentTab = 1;

View File

@ -20,7 +20,7 @@ import {
import { Icons } from './SvgUtils';
export const defaultFields = `${TabSpecificField.FOLLOWERS}, ${TabSpecificField.TAGS}, ${TabSpecificField.OWNER},
${TabSpecificField.TASKS}, ${TabSpecificField.PIPELINE_STATUS}`;
${TabSpecificField.TASKS}, ${TabSpecificField.PIPELINE_STATUS},${TabSpecificField.EXTENSION}`;
export const pipelineDetailsTabs = [
{
@ -37,6 +37,10 @@ export const pipelineDetailsTabs = [
path: 'lineage',
field: TabSpecificField.LINEAGE,
},
{
name: 'Custom Properties',
path: 'custom_properties',
},
{
name: 'Manage',
path: 'manage',
@ -54,10 +58,14 @@ export const getCurrentPipelineTab = (tab: string) => {
case 'lineage':
currentTab = 3;
break;
case 'custom_properties':
currentTab = 4;
break;
case 'manage':
currentTab = 4;
currentTab = 5;
break;

View File

@ -37,6 +37,10 @@ export const topicDetailsTabs = [
path: 'lineage',
field: TabSpecificField.LINEAGE,
},
{
name: 'Custom Properties',
path: 'custom_properties',
},
{
name: 'Manage',
path: 'manage',
@ -62,9 +66,13 @@ export const getCurrentTopicTab = (tab: string) => {
currentTab = 5;
break;
case 'manage':
case 'custom_properties':
currentTab = 6;
break;
case 'manage':
currentTab = 7;
break;
case 'schema':