From 331f043d3bfbc93bee24f295d52f22394d9b4ad8 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Tue, 11 Apr 2023 14:04:10 +0530 Subject: [PATCH] Fixes #10584 : Improvements in DataModel Entity (#10983) * Improvements in DataModel Entity * Added lineage view in DataModel Entity * changes as per commets * fix unit test issue * fix code smell * changes as per commets --- .../ui/src/assets/svg/data-model.svg | 4 +- .../DataModels/DataModelDetails.component.tsx | 389 ++++++++++++++++++ .../DataModels/DataModelDetails.interface.tsx | 64 +++ .../components/DataModels/DataModelsTable.tsx | 1 + .../ModelTab/ModelTab.component.tsx | 4 + .../EntityInfoDrawer.component.tsx | 12 + .../EntityLineage/EntityLineage.component.tsx | 19 +- .../EntityLineage/LineageNodeLabel.tsx | 8 +- .../DashboardSummary.component.tsx | 24 ++ .../DashboardSummary.test.tsx | 4 +- .../DataModelSummary.component.tsx | 158 +++++++ .../DataModelSummary.interface.tsx | 28 ++ .../EntitySummaryPanel.style.less | 1 + .../components/Explore/explore.interface.ts | 4 +- .../RichTextEditorPreviewer.less | 3 +- .../resources/ui/src/enums/Explore.enum.ts | 1 + .../resources/ui/src/enums/entity.enum.ts | 1 + .../resources/ui/src/enums/search.enum.ts | 1 - .../ui/src/interface/search.interface.ts | 1 - .../ui/src/locale/languages/en-us.json | 2 + .../ui/src/locale/languages/es-es.json | 2 + .../ui/src/locale/languages/fr-fr.json | 2 + .../ui/src/locale/languages/ja-jp.json | 2 + .../ui/src/locale/languages/pt-br.json | 2 + .../ui/src/locale/languages/zh-cn.json | 2 + .../DataModelPage/DataModelPage.component.tsx | 317 ++------------ .../resources/ui/src/pages/service/index.tsx | 2 +- .../resources/ui/src/rest/dataModelsAPI.ts | 11 + .../ui/src/utils/EntitySummaryPanelUtils.tsx | 19 +- .../resources/ui/src/utils/EntityUtils.tsx | 65 ++- .../resources/ui/src/utils/TableUtils.tsx | 1 - .../main/resources/ui/src/utils/TasksUtils.ts | 3 + .../mocks/EntitySummaryPanelUtils.mock.tsx | 2 +- 33 files changed, 861 insertions(+), 298 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.interface.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-model.svg b/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-model.svg index 14ab49ca8c3..38e76846de5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-model.svg +++ b/openmetadata-ui/src/main/resources/ui/src/assets/svg/data-model.svg @@ -1,6 +1,6 @@ - + - + diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx new file mode 100644 index 00000000000..94b753ed9d1 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.component.tsx @@ -0,0 +1,389 @@ +/* + * Copyright 2023 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 { Card, Col, Row, Space, Tabs } from 'antd'; +import ActivityFeedList from 'components/ActivityFeed/ActivityFeedList/ActivityFeedList'; +import ActivityThreadPanel from 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; +import Description from 'components/common/description/Description'; +import EntityPageInfo from 'components/common/entityPageInfo/EntityPageInfo'; +import PageContainerV1 from 'components/containers/PageContainerV1'; +import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component'; +import Loader from 'components/Loader/Loader'; +import SchemaEditor from 'components/schema-editor/SchemaEditor'; +import { FQN_SEPARATOR_CHAR } from 'constants/char.constants'; +import { getServiceDetailsPath } from 'constants/constants'; +import { EntityField } from 'constants/Feeds.constants'; +import { observerOptions } from 'constants/Mydata.constants'; +import { CSMode } from 'enums/codemirror.enum'; +import { EntityInfo, EntityType } from 'enums/entity.enum'; +import { ServiceCategory } from 'enums/service.enum'; +import { OwnerType } from 'enums/user.enum'; +import { Paging } from 'generated/type/paging'; +import { useInfiniteScroll } from 'hooks/useInfiniteScroll'; +import { ExtraInfo } from 'Models'; +import { DATA_MODELS_DETAILS_TABS } from 'pages/DataModelPage/DataModelsInterface'; +import React, { RefObject, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + getCountBadge, + getCurrentUserId, + getEntityPlaceHolder, + getOwnerValue, +} from 'utils/CommonUtils'; +import { getEntityName } from 'utils/EntityUtils'; +import { getEntityFieldThreadCounts } from 'utils/FeedUtils'; +import { serviceTypeLogo } from 'utils/ServiceUtils'; +import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils'; +import { DataModelDetailsProps } from './DataModelDetails.interface'; +import ModelTab from './ModelTab/ModelTab.component'; + +const DataModelDetails = ({ + isEntityThreadLoading, + paging, + entityFieldTaskCount, + entityFieldThreadCount, + entityThread, + feedCount, + dataModelData, + dashboardDataModelFQN, + postFeedHandler, + dataModelPermissions, + createThread, + deletePostHandler, + updateThreadHandler, + handleFollowDataModel, + handleRemoveTier, + fetchFeedHandler, + handleUpdateTags, + handleUpdateOwner, + handleUpdateTier, + activeTab, + handleTabChange, + handleUpdateDescription, + handleUpdateDataModel, + handleFeedFilterChange, +}: DataModelDetailsProps) => { + const { t } = useTranslation(); + const [elementRef, isInView] = useInfiniteScroll(observerOptions); + const [isEditDescription, setIsEditDescription] = useState(false); + const [threadLink, setThreadLink] = useState(''); + + const loader = useMemo( + () => (isEntityThreadLoading ? : null), + [isEntityThreadLoading] + ); + + const { + hasEditDescriptionPermission, + hasEditOwnerPermission, + hasEditTagsPermission, + hasEditTierPermission, + hasEditLineagePermission, + } = useMemo(() => { + return { + hasEditDescriptionPermission: + dataModelPermissions.EditAll || dataModelPermissions.EditDescription, + hasEditOwnerPermission: + dataModelPermissions.EditAll || dataModelPermissions.EditOwner, + hasEditTagsPermission: + dataModelPermissions.EditAll || dataModelPermissions.EditTags, + hasEditTierPermission: + dataModelPermissions.EditAll || dataModelPermissions.EditTier, + hasEditLineagePermission: + dataModelPermissions.EditAll || dataModelPermissions.EditLineage, + }; + }, [dataModelPermissions]); + + const { + tier, + deleted, + owner, + description, + version, + tags, + entityName, + entityId, + followers, + dataModelType, + isUserFollowing, + } = useMemo(() => { + return { + deleted: dataModelData?.deleted, + owner: dataModelData?.owner, + description: dataModelData?.description, + version: dataModelData?.version, + tier: getTierTags(dataModelData?.tags ?? []), + tags: getTagsWithoutTier(dataModelData?.tags ?? []), + entityId: dataModelData?.id, + entityName: getEntityName(dataModelData), + isUserFollowing: dataModelData?.followers?.some( + ({ id }: { id: string }) => id === getCurrentUserId() + ), + followers: dataModelData?.followers ?? [], + dataModelType: dataModelData?.dataModelType, + }; + }, [dataModelData]); + + const breadcrumbTitles = useMemo(() => { + const serviceType = dataModelData?.serviceType; + const service = dataModelData?.service; + const serviceName = service?.name; + + return [ + { + name: serviceName || '', + url: serviceName + ? getServiceDetailsPath( + serviceName, + ServiceCategory.DASHBOARD_SERVICES + ) + : '', + imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined, + }, + { + name: entityName, + url: '', + activeTitle: true, + }, + ]; + }, [dataModelData, dashboardDataModelFQN, entityName]); + + const extraInfo: Array = [ + { + key: EntityInfo.OWNER, + value: owner && getOwnerValue(owner), + placeholderText: getEntityPlaceHolder( + getEntityName(owner), + owner?.deleted + ), + isLink: true, + openInNewTab: false, + profileName: owner?.type === OwnerType.USER ? owner?.name : undefined, + }, + { + key: EntityInfo.TIER, + value: tier?.tagFQN ? tier.tagFQN.split(FQN_SEPARATOR_CHAR)[1] : '', + }, + { + key: EntityInfo.DATA_MODEL_TYPE, + value: dataModelType, + showLabel: true, + }, + ]; + + const onThreadLinkSelect = (link: string) => { + setThreadLink(link); + }; + + const onThreadPanelClose = () => { + setThreadLink(''); + }; + + const fetchMoreThread = ( + isElementInView: boolean, + pagingObj: Paging, + isLoading: boolean + ) => { + if ( + isElementInView && + pagingObj?.after && + !isLoading && + activeTab === DATA_MODELS_DETAILS_TABS.ACTIVITY + ) { + fetchFeedHandler(pagingObj.after); + } + }; + + useEffect(() => { + fetchMoreThread(isInView as boolean, paging, isEntityThreadLoading); + }, [paging, isEntityThreadLoading, isInView]); + + return ( + +
+ + + + {t('label.model')} + + }> + + + setIsEditDescription(false)} + onDescriptionEdit={() => setIsEditDescription(true)} + onDescriptionUpdate={handleUpdateDescription} + onThreadLinkSelect={onThreadLinkSelect} + /> + + + + + + + + {t('label.activity-feed-and-task-plural')}{' '} + {getCountBadge( + feedCount, + '', + DATA_MODELS_DETAILS_TABS.ACTIVITY === activeTab + )} + + }> + + + +
+ +
+ +
+ {loader} +
+
+ {dataModelData?.sql && ( + + {t('label.sql-uppercase')} + + }> + + + + + )} + + + {t('label.lineage')} + + }> + + + + +
+ + {threadLink ? ( + + ) : null} + +
} + /> +
+ + ); +}; + +export default DataModelDetails; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.interface.tsx new file mode 100644 index 00000000000..552d855de6f --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelDetails.interface.tsx @@ -0,0 +1,64 @@ +/* + * Copyright 2023 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 { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface'; +import { FeedFilter } from 'enums/mydata.enum'; +import { CreateThread, ThreadType } from 'generated/api/feed/createThread'; +import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; +import { Column } from 'generated/entity/data/table'; +import { Thread } from 'generated/entity/feed/thread'; +import { Paging } from 'generated/type/paging'; +import { + EntityFieldThreadCount, + ThreadUpdatedFunc, +} from 'interface/feed.interface'; +import { EntityTags } from 'Models'; + +export interface DataModelDetailsProps { + isEntityThreadLoading: boolean; + + paging: Paging; + entityFieldThreadCount: EntityFieldThreadCount[]; + entityFieldTaskCount: EntityFieldThreadCount[]; + entityThread: Thread[]; + feedCount: number; + dataModelData?: DashboardDataModel; + dashboardDataModelFQN: string; + postFeedHandler: (value: string, id: string) => void; + dataModelPermissions: OperationPermission; + createThread: (data: CreateThread) => void; + deletePostHandler: ( + threadId: string, + postId: string, + isThread: boolean + ) => void; + updateThreadHandler: ThreadUpdatedFunc; + handleFollowDataModel: () => void; + handleRemoveTier: () => void; + fetchFeedHandler: ( + after?: string, + feedType?: FeedFilter, + threadType?: ThreadType + ) => void; + handleUpdateTags: (selectedTags?: Array) => void; + handleUpdateOwner: (updatedOwner?: DashboardDataModel['owner']) => void; + handleUpdateTier: (updatedTier?: string) => void; + activeTab: string; + handleTabChange: (tabValue: string) => void; + handleUpdateDescription: (value: string) => Promise; + handleUpdateDataModel: (updatedDataModel: Column[]) => Promise; + handleFeedFilterChange: ( + feedType: FeedFilter, + threadType?: ThreadType + ) => void; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelsTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelsTable.tsx index 9ec154e4f0c..3aef9bb933a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelsTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/DataModelsTable.tsx @@ -36,6 +36,7 @@ const DataModelTable = ({ data, isLoading }: DataModelTableProps) => { title: t('label.name'), dataIndex: 'displayName', key: 'displayName', + width: 350, render: (_, record: ServicePageData) => { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/ModelTab/ModelTab.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/ModelTab/ModelTab.component.tsx index 5069502ed4b..32dbf19dc7d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataModels/ModelTab/ModelTab.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataModels/ModelTab/ModelTab.component.tsx @@ -26,6 +26,7 @@ import { updateDataModelColumnDescription, updateDataModelColumnTags, } from 'utils/DataModelsUtils'; +import { getEntityName } from 'utils/EntityUtils'; import { fetchTagsAndGlossaryTerms } from 'utils/TagsUtils'; import { ReactComponent as EditIcon } from '../../../assets/svg/ic-edit.svg'; import { ModelTabProps } from './ModelTab.interface'; @@ -195,6 +196,9 @@ const ModelTab = ({ dataIndex: 'name', key: 'name', width: 250, + render: (_, record) => ( + {getEntityName(record)} + ), }, { title: t('label.type'), diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityInfoDrawer/EntityInfoDrawer.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityInfoDrawer/EntityInfoDrawer.component.tsx index 2f39aa763fc..4d0984e7730 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityInfoDrawer/EntityInfoDrawer.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityInfoDrawer/EntityInfoDrawer.component.tsx @@ -16,12 +16,14 @@ import { Col, Drawer, Row, Typography } from 'antd'; import classNames from 'classnames'; import ContainerSummary from 'components/Explore/EntitySummaryPanel/ContainerSummary/ContainerSummary.component'; import DashboardSummary from 'components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component'; +import DataModelSummary from 'components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component'; import MlModelSummary from 'components/Explore/EntitySummaryPanel/MlModelSummary/MlModelSummary.component'; import PipelineSummary from 'components/Explore/EntitySummaryPanel/PipelineSummary/PipelineSummary.component'; import TableSummary from 'components/Explore/EntitySummaryPanel/TableSummary/TableSummary.component'; import TopicSummary from 'components/Explore/EntitySummaryPanel/TopicSummary/TopicSummary.component'; import { FQN_SEPARATOR_CHAR } from 'constants/char.constants'; import { Container } from 'generated/entity/data/container'; +import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; import { Mlmodel } from 'generated/entity/data/mlmodel'; import { EntityDetailUnion } from 'Models'; import React, { useEffect, useMemo, useState } from 'react'; @@ -209,6 +211,16 @@ const EntityInfoDrawer = ({ /> ); + case EntityType.DASHBOARD_DATA_MODEL: + return ( + + ); + default: return null; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx index 2c6644543c1..c8aa25bb112 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/EntityLineage.component.tsx @@ -51,6 +51,7 @@ import ReactFlow, { useEdgesState, useNodesState, } from 'reactflow'; +import { getDataModelDetails } from 'rest/dataModelsAPI'; import { getLineageByFQN } from 'rest/lineageAPI'; import { searchData } from 'rest/miscAPI'; import { getTableDetails } from 'rest/tableAPI'; @@ -629,6 +630,16 @@ const EntityLineageComponent: FunctionComponent = ({ [nodes, updatedLineageData] ); + const getColumnsForNode = async (type: string, id: string) => { + const fields = ['columns']; + + if (type === EntityType.DASHBOARD_DATA_MODEL) { + return await getDataModelDetails(id, fields); + } + + return await getTableDetails(id, fields); + }; + /** * take node and get the columns for that node * @param expandNode @@ -636,11 +647,11 @@ const EntityLineageComponent: FunctionComponent = ({ const getTableColumns = async (expandNode?: EntityReference) => { if (expandNode) { try { - const res = await getTableDetails(expandNode.id, ['columns']); - const tableId = expandNode.id; + const res = await getColumnsForNode(expandNode.type, expandNode.id); + const nodeId = expandNode.id; const { columns } = res; - tableColumnsRef.current[tableId] = columns; - updateColumnsToNode(columns, tableId); + tableColumnsRef.current[nodeId] = columns; + updateColumnsToNode(columns, nodeId); } catch (error) { showErrorToast( error as AxiosError, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/LineageNodeLabel.tsx b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/LineageNodeLabel.tsx index dfa0de42813..28a13d5d2f4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/LineageNodeLabel.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/EntityLineage/LineageNodeLabel.tsx @@ -12,7 +12,7 @@ */ import { Button } from 'antd'; -import { EntityLineageNodeType } from 'enums/entity.enum'; +import { EntityLineageNodeType, EntityType } from 'enums/entity.enum'; import { get } from 'lodash'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -32,7 +32,11 @@ const TableExpandButton = ({ onNodeExpand, isExpanded, }: LineageNodeLabelProps) => { - if (node.type !== 'table') { + if ( + ![EntityType.TABLE, EntityType.DASHBOARD_DATA_MODEL].includes( + node.type as EntityType + ) + ) { return null; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx index dd014512b2a..7f3062604a1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.component.tsx @@ -85,6 +85,15 @@ function DashboardSummary({ [charts] ); + const formattedDataModelData: BasicEntityInfo[] = useMemo( + () => + getFormattedEntityData( + SummaryEntityType.COLUMN, + entityDetails.dataModels + ), + [charts] + ); + const isExplore = useMemo( () => componentType === DRAWER_NAVIGATION_OPTIONS.explore, [componentType] @@ -173,6 +182,21 @@ function DashboardSummary({ + + + + + + + {t('label.data-model-plural')} + + + + + + ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx index 33849f3ce58..85fe9cb36fe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DashboardSummary/DashboardSummary.test.tsx @@ -45,13 +45,13 @@ describe('DashboardSummary component tests', () => { const dashboardUrlValue = screen.getByTestId('dashboard-url-value'); const dashboardLinkName = screen.getByTestId('dashboard-link-name'); const chartsHeader = screen.getByTestId('charts-header'); - const summaryList = screen.getByTestId('SummaryList'); + const summaryList = screen.getAllByTestId('SummaryList'); expect(dashboardLinkName).toBeInTheDocument(); expect(dashboardUrlLabel).toBeInTheDocument(); expect(dashboardUrlValue).toContainHTML(mockDashboardEntityDetails.name); expect(chartsHeader).toBeInTheDocument(); - expect(summaryList).toBeInTheDocument(); + expect(summaryList).toHaveLength(2); }); it('Component should render properly, when loaded in the Lineage page.', async () => { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx new file mode 100644 index 00000000000..0e2dc272c70 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.component.tsx @@ -0,0 +1,158 @@ +/* + * Copyright 2023 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 { Col, Divider, Row, Typography } from 'antd'; +import classNames from 'classnames'; +import SummaryTagsDescription from 'components/common/SummaryTagsDescription/SummaryTagsDescription.component'; +import SummaryPanelSkeleton from 'components/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component'; +import { ExplorePageTabs } from 'enums/Explore.enum'; +import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; +import { isEmpty } from 'lodash'; +import { default as React, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { + DRAWER_NAVIGATION_OPTIONS, + getEntityOverview, +} from 'utils/EntityUtils'; +import SVGIcons from 'utils/SvgUtils'; +import { SummaryEntityType } from '../../../../enums/EntitySummary.enum'; +import { getFormattedEntityData } from '../../../../utils/EntitySummaryPanelUtils'; +import SummaryList from '../SummaryList/SummaryList.component'; +import { BasicEntityInfo } from '../SummaryList/SummaryList.interface'; +import { DataModelSummaryProps } from './DataModelSummary.interface'; + +const DataModelSummary = ({ + entityDetails, + componentType = DRAWER_NAVIGATION_OPTIONS.explore, + tags, + isLoading, +}: DataModelSummaryProps) => { + const { t } = useTranslation(); + const { columns } = entityDetails; + const [dataModelDetails, setDataModelDetails] = + useState(entityDetails); + + const isExplore = useMemo( + () => componentType === DRAWER_NAVIGATION_OPTIONS.explore, + [componentType] + ); + + const entityInfo = useMemo( + () => + getEntityOverview(ExplorePageTabs.DASHBOARD_DATA_MODEL, dataModelDetails), + [dataModelDetails] + ); + + const formattedColumnsData: BasicEntityInfo[] = useMemo( + () => getFormattedEntityData(SummaryEntityType.COLUMN, columns), + [columns, dataModelDetails] + ); + + useEffect(() => { + if (!isEmpty(entityDetails)) { + setDataModelDetails(entityDetails); + } + }, [entityDetails]); + + return ( + + <> + + + + {entityInfo.map((info) => { + const isOwner = info.name === t('label.owner'); + + return info.visible?.includes(componentType) ? ( + + + {!isOwner ? ( + + + {info.name} + + + ) : null} + + {info.isLink ? ( + + {info.value} + {info.isExternal ? ( + + ) : null} + + ) : ( + + {info.value} + + )} + + + + ) : null; + })} + + + + + + + {!isExplore ? ( + <> + + + + ) : null} + + + + + + + {t('label.column-plural')} + + + + + + + + + ); +}; + +export default DataModelSummary; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx new file mode 100644 index 00000000000..2d8948e9077 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/DataModelSummary/DataModelSummary.interface.tsx @@ -0,0 +1,28 @@ +/* + * Copyright 2023 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 { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; +import { TableType, TagLabel } from '../../../../generated/entity/data/table'; + +export interface DataModelSummaryProps { + entityDetails: DashboardDataModel; + componentType?: string; + tags?: TagLabel[]; + isLoading?: boolean; +} + +export interface BasicTableInfo { + Type: TableType | string; + Queries: string; + Columns: string; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.style.less b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.style.less index f2ab77d9f6c..568e31a747d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.style.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.style.less @@ -30,6 +30,7 @@ border: @global-border; font-size: 14px; overflow-y: scroll; + overflow-x: hidden; -ms-overflow-style: none; scrollbar-width: none; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts index d10ff74480b..31c9851e769 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/explore.interface.ts @@ -15,6 +15,7 @@ import { DefaultOptionType } from 'antd/lib/select'; import { SORT_ORDER } from 'enums/common.enum'; import { Tag } from 'generated/entity/classification/tag'; import { Container } from 'generated/entity/data/container'; +import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; import { Database } from 'generated/entity/data/database'; import { DatabaseSchema } from 'generated/entity/data/databaseSchema'; import { Glossary } from 'generated/entity/data/glossary'; @@ -122,7 +123,8 @@ export type EntityUnion = | DatabaseSchema | Database | Glossary - | Tag; + | Tag + | DashboardDataModel; export interface EntityDetailsObjectInterface { details: EntityUnion; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.less b/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.less index 38142d42b29..25e20f3d964 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/rich-text-editor/RichTextEditorPreviewer.less @@ -90,8 +90,7 @@ } p { - margin-top: 10px; - margin-bottom: 0px; + margin: 0px; color: @body-text-color; word-break: break-word; line-height: 20px; diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts index 9482a674584..996de6149d5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/Explore.enum.ts @@ -25,4 +25,5 @@ export enum ExplorePageTabs { CONTAINERS = 'containers', GLOSSARY = 'glossary', TAG = 'tag', + DASHBOARD_DATA_MODEL = 'dashboardDataModel', } diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts index dc39e7e3153..e130f732e5b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/entity.enum.ts @@ -123,4 +123,5 @@ export enum EntityInfo { MAX_MESSAGE_SIZE = 'Max Message Size', SIZE = 'size', NUMBER_OF_OBJECTS = 'number-of-object-plural', + DATA_MODEL_TYPE = 'data-model-type', } diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/search.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/search.enum.ts index 953c5e41d20..174b37ce418 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/search.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/search.enum.ts @@ -23,5 +23,4 @@ export enum SearchIndex { TAG = 'tag_search_index', CONTAINER = 'container_search_index', QUERY = 'query_search_index', - DASHBOARD_DATA_MODEL = 'dashboard_data_model_search_index', } diff --git a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts index ec0dd7c9d9c..b6700aa96a3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts @@ -106,7 +106,6 @@ export type SearchIndexSearchSourceMapping = { [SearchIndex.TAG]: TagClassSearchSource; [SearchIndex.CONTAINER]: ContainerSearchSource; [SearchIndex.QUERY]: QuerySearchSource; - [SearchIndex.DASHBOARD_DATA_MODEL]: dashboardDataModelSearchSource; }; export type SearchRequest< diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 5192f2b9443..1cb4b50fc61 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -182,6 +182,8 @@ "data-insight-top-viewed-entity-summary": "Most Viewed Data Assets", "data-insight-total-entity-summary": "Total Data Assets", "data-model": "Data Model", + "data-model-plural": "Data Models", + "data-model-type": "Data Model Type", "data-quality-test": "Data Quality Test", "data-type": "Data Type", "database": "Database", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index 45ea7dd252d..f0850557e18 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -182,6 +182,8 @@ "data-insight-top-viewed-entity-summary": "Activos de datos más vistos", "data-insight-total-entity-summary": "Total de activos de datos", "data-model": "Data Model", + "data-model-plural": "Data Models", + "data-model-type": "Data Model Type", "data-quality-test": "Prueba de calidad de datos", "data-type": "Tipo de datos", "database": "Base de datos", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index b2dc403646d..ff983d8ad88 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -182,6 +182,8 @@ "data-insight-top-viewed-entity-summary": "Resources de Données les plus Visitées", "data-insight-total-entity-summary": "Total Resources de Données", "data-model": "Data Model", + "data-model-plural": "Data Models", + "data-model-type": "Data Model Type", "data-quality-test": "Data Quality Test", "data-type": "Type de donnée", "database": "Base de Données", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 293f77c4138..998707d7e4d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -182,6 +182,8 @@ "data-insight-top-viewed-entity-summary": "最も閲覧されたデータアセット", "data-insight-total-entity-summary": "全てのデータアセット", "data-model": "Data Model", + "data-model-plural": "Data Models", + "data-model-type": "Data Model Type", "data-quality-test": "データ品質テスト", "data-type": "データ型", "database": "データベース", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index 5d08731d143..e74eae495d8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -182,6 +182,8 @@ "data-insight-top-viewed-entity-summary": "Recursos de dados mais visualizados", "data-insight-total-entity-summary": "Total de recursos de dados", "data-model": "Data Model", + "data-model-plural": "Data Models", + "data-model-type": "Data Model Type", "data-quality-test": "Teste de qualidade de dados", "data-type": "Tipo de dado", "database": "Banco de dados", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 1f018278e73..b1713e21237 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -182,6 +182,8 @@ "data-insight-top-viewed-entity-summary": "查看次数最多的数据资产", "data-insight-total-entity-summary": "所有数据资产", "data-model": "Data Model", + "data-model-plural": "Data Models", + "data-model-type": "Data Model Type", "data-quality-test": "Data Quality Test", "data-type": "Data Type", "database": "数据库", diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx index fecac54cd2d..10027896326 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx @@ -11,33 +11,19 @@ * limitations under the License. */ -import { Card, Col, Row, Space, Tabs } from 'antd'; import AppState from 'AppState'; import { AxiosError } from 'axios'; -import ActivityFeedList from 'components/ActivityFeed/ActivityFeedList/ActivityFeedList'; -import ActivityThreadPanel from 'components/ActivityFeed/ActivityThreadPanel/ActivityThreadPanel'; -import Description from 'components/common/description/Description'; -import EntityPageInfo from 'components/common/entityPageInfo/EntityPageInfo'; import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder'; -import PageContainerV1 from 'components/containers/PageContainerV1'; -import ModelTab from 'components/DataModels/ModelTab/ModelTab.component'; -import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component'; +import DataModelDetails from 'components/DataModels/DataModelDetails.component'; 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 { FQN_SEPARATOR_CHAR } from 'constants/char.constants'; -import { getServiceDetailsPath } from 'constants/constants'; -import { EntityField } from 'constants/Feeds.constants'; import { NO_PERMISSION_TO_VIEW } from 'constants/HelperTextUtil'; -import { CSMode } from 'enums/codemirror.enum'; -import { EntityInfo, EntityType } from 'enums/entity.enum'; +import { EntityType } from 'enums/entity.enum'; import { FeedFilter } from 'enums/mydata.enum'; -import { ServiceCategory } from 'enums/service.enum'; -import { OwnerType } from 'enums/user.enum'; import { compare, Operation } from 'fast-json-patch'; import { CreateThread } from 'generated/api/feed/createThread'; import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; @@ -47,8 +33,15 @@ import { LabelType, State, TagSource } from 'generated/type/tagLabel'; import { EntityFieldThreadCount } from 'interface/feed.interface'; import jsonData from 'jsons/en'; import { isUndefined, omitBy } from 'lodash'; -import { EntityTags, ExtraInfo } from 'Models'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { observer } from 'mobx-react'; +import { EntityTags } from 'Models'; +import { + default as React, + useCallback, + useEffect, + useMemo, + useState, +} from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory, useParams } from 'react-router-dom'; import { @@ -59,22 +52,14 @@ import { } from 'rest/dataModelsAPI'; import { getAllFeeds, postFeedById, postThread } from 'rest/feedsAPI'; import { - getCountBadge, getCurrentUserId, getEntityMissingError, - getEntityPlaceHolder, getFeedCounts, - getOwnerValue, } from 'utils/CommonUtils'; import { getDataModelsDetailPath } from 'utils/DataModelsUtils'; -import { getEntityFeedLink, getEntityName } from 'utils/EntityUtils'; -import { - deletePost, - getEntityFieldThreadCounts, - updateThreadData, -} from 'utils/FeedUtils'; +import { getEntityFeedLink } from 'utils/EntityUtils'; +import { deletePost, updateThreadData } from 'utils/FeedUtils'; import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils'; -import { serviceTypeLogo } from 'utils/ServiceUtils'; import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils'; import { showErrorToast } from 'utils/ToastUtils'; import { DATA_MODELS_DETAILS_TABS } from './DataModelsInterface'; @@ -86,14 +71,12 @@ const DataModelsPage = () => { const { getEntityPermissionByFqn } = usePermissionProvider(); const { dashboardDataModelFQN, tab } = useParams() as Record; - const [isEditDescription, setIsEditDescription] = useState(false); const [isLoading, setIsLoading] = useState(false); const [hasError, setHasError] = useState(false); const [dataModelPermissions, setDataModelPermissions] = useState(DEFAULT_ENTITY_PERMISSION); const [dataModelData, setDataModelData] = useState(); - const [threadLink, setThreadLink] = useState(''); const [entityThread, setEntityThread] = useState([]); const [isEntityThreadLoading, setIsEntityThreadLoading] = useState(false); @@ -114,82 +97,22 @@ const DataModelsPage = () => { [AppState.userDetails, AppState.nonSecureUserDetails] ); - const { - hasViewPermission, - hasEditDescriptionPermission, - hasEditOwnerPermission, - hasEditTagsPermission, - hasEditTierPermission, - hasEditLineagePermission, - } = useMemo(() => { + const { hasViewPermission } = useMemo(() => { return { hasViewPermission: dataModelPermissions.ViewAll || dataModelPermissions.ViewBasic, - hasEditDescriptionPermission: - dataModelPermissions.EditAll || dataModelPermissions.EditDescription, - hasEditOwnerPermission: - dataModelPermissions.EditAll || dataModelPermissions.EditOwner, - hasEditTagsPermission: - dataModelPermissions.EditAll || dataModelPermissions.EditTags, - hasEditTierPermission: - dataModelPermissions.EditAll || dataModelPermissions.EditTier, - hasEditLineagePermission: - dataModelPermissions.EditAll || dataModelPermissions.EditLineage, }; }, [dataModelPermissions]); - const { - tier, - deleted, - owner, - description, - version, - tags, - entityName, - entityId, - followers, - isUserFollowing, - } = useMemo(() => { + const { tier, isUserFollowing } = useMemo(() => { return { - deleted: dataModelData?.deleted, - owner: dataModelData?.owner, - description: dataModelData?.description, - version: dataModelData?.version, tier: getTierTags(dataModelData?.tags ?? []), - tags: getTagsWithoutTier(dataModelData?.tags ?? []), - entityId: dataModelData?.id, - entityName: getEntityName(dataModelData), isUserFollowing: dataModelData?.followers?.some( ({ id }: { id: string }) => id === getCurrentUserId() ), - followers: dataModelData?.followers ?? [], }; }, [dataModelData]); - const breadcrumbTitles = useMemo(() => { - const serviceType = dataModelData?.serviceType; - const service = dataModelData?.service; - const serviceName = service?.name; - - return [ - { - name: serviceName || '', - url: serviceName - ? getServiceDetailsPath( - serviceName, - ServiceCategory.DASHBOARD_SERVICES - ) - : '', - imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined, - }, - { - name: entityName, - url: '', - activeTitle: true, - }, - ]; - }, [dataModelData, dashboardDataModelFQN, entityName]); - const getFeedData = useCallback( async ( after?: string, @@ -245,10 +168,6 @@ const DataModelsPage = () => { deletePost(threadId, postId, isThread, setEntityThread); }; - const onThreadLinkSelect = (link: string) => { - setThreadLink(link); - }; - const postFeedHandler = (value: string, id: string) => { const currentUser = AppState.userDetails?.name ?? AppState.users[0]?.name; @@ -348,10 +267,6 @@ const DataModelsPage = () => { } }; - const onThreadPanelClose = () => { - setThreadLink(''); - }; - const handleTabChange = (tabValue: string) => { if (tabValue !== tab) { history.push({ @@ -379,6 +294,7 @@ const DataModelsPage = () => { description: newDescription, version, })); + getEntityFeedCount(); } catch (error) { showErrorToast(error as AxiosError); } @@ -412,24 +328,6 @@ const DataModelsPage = () => { } }; - const extraInfo: Array = [ - { - key: EntityInfo.OWNER, - value: owner && getOwnerValue(owner), - placeholderText: getEntityPlaceHolder( - getEntityName(owner), - owner?.deleted - ), - isLink: true, - openInNewTab: false, - profileName: owner?.type === OwnerType.USER ? owner?.name : undefined, - }, - { - key: EntityInfo.TIER, - value: tier?.tagFQN ? tier.tagFQN.split(FQN_SEPARATOR_CHAR)[1] : '', - }, - ]; - const handleRemoveTier = async () => { try { const { tags: newTags, version } = await handleUpdateDataModelData({ @@ -568,162 +466,33 @@ const DataModelsPage = () => { } return ( - -
- - - - {t('label.model')} - - }> - - - setIsEditDescription(false)} - onDescriptionEdit={() => setIsEditDescription(true)} - onDescriptionUpdate={handleUpdateDescription} - /> - - - - - - - - {t('label.activity-feed-and-task-plural')}{' '} - {getCountBadge( - feedCount, - '', - DATA_MODELS_DETAILS_TABS.ACTIVITY === tab - )} - - }> - - - -
- -
- -
-
-
- {dataModelData?.sql && ( - - {t('label.sql-uppercase')} - - }> - - - - - )} - - - {t('label.lineage')} - - }> - - - - -
- - {threadLink ? ( - - ) : null} -
-
+ ); }; -export default DataModelsPage; +export default observer(DataModelsPage); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx index 76df239c269..7b021db47a8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/service/index.tsx @@ -195,7 +195,7 @@ const ServicePage: FunctionComponent = () => { servicePermission, dataModelPaging.total ), - [serviceName, paging, ingestions, servicePermission] + [serviceName, paging, ingestions, servicePermission, dataModelPaging] ); const extraInfo: Array = [ diff --git a/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts b/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts index e1567077265..36d5d1ed378 100644 --- a/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts +++ b/openmetadata-ui/src/main/resources/ui/src/rest/dataModelsAPI.ts @@ -27,6 +27,17 @@ const configOptions = { headers: { 'Content-type': 'application/json' }, }; +export const getDataModelDetails = async ( + id: string, + arrQueryFields: string | string[] +) => { + const url = getURLWithQueryFields(`${URL}/${id}`, arrQueryFields); + + const response = await APIClient.get(url); + + return response.data; +}; + export const getDataModelsByName = async ( name: string, fields: string | string[] diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx index 16dbca771d5..2d803c23094 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntitySummaryPanelUtils.tsx @@ -12,6 +12,7 @@ */ import { Space, Typography } from 'antd'; +import { NO_DATA_PLACEHOLDER } from 'constants/constants'; import { isEmpty } from 'lodash'; import React from 'react'; import { Link } from 'react-router-dom'; @@ -27,6 +28,14 @@ import SVGIcons from './SvgUtils'; const { Text } = Typography; +export interface EntityNameProps { + name?: string; + displayName?: string; +} + +const getTitleName = (data: EntityNameProps) => + getEntityName(data) || NO_DATA_PLACEHOLDER; + export const getFormattedEntityData = ( entityType: SummaryEntityType, entityInfo?: Array, @@ -39,7 +48,7 @@ export const getFormattedEntityData = ( case SummaryEntityType.COLUMN: { return (entityInfo as Column[]).map((column) => ({ name: column.name, - title: {column.name}, + title: {getTitleName(column)}, type: column.dataType, tags: column.tags, description: column.description, @@ -58,7 +67,7 @@ export const getFormattedEntityData = ( - {getEntityName(chart)} + {getTitleName(chart)} @@ -76,7 +85,7 @@ export const getFormattedEntityData = ( - {task.name} + {getTitleName(task)} @@ -91,7 +100,7 @@ export const getFormattedEntityData = ( return (entityInfo as MlFeature[]).map((feature) => ({ algorithm: feature.featureAlgorithm, name: feature.name || '--', - title: {feature.name}, + title: {getTitleName(feature)}, type: feature.dataType, tags: feature.tags, description: feature.description, @@ -100,7 +109,7 @@ export const getFormattedEntityData = ( case SummaryEntityType.SCHEMAFIELD: { return (entityInfo as Field[]).map((field) => ({ name: field.name, - title: {field.name}, + title: {getTitleName(field)}, type: field.dataType, description: field.description, tags: field.tags, diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx index d00e82d1c3c..d1ef61b506d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -21,6 +21,7 @@ import { EntityUnion } from 'components/Explore/explore.interface'; import { ResourceEntity } from 'components/PermissionProvider/PermissionProvider.interface'; import { ExplorePageTabs } from 'enums/Explore.enum'; import { Container } from 'generated/entity/data/container'; +import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel'; import { Mlmodel } from 'generated/entity/data/mlmodel'; import i18next from 'i18next'; import { get, isEmpty, isNil, isUndefined, lowerCase, startCase } from 'lodash'; @@ -98,7 +99,8 @@ export const getEntityTags = ( case EntityType.PIPELINE: case EntityType.DASHBOARD: case EntityType.TOPIC: - case EntityType.MLMODEL: { + case EntityType.MLMODEL: + case EntityType.DASHBOARD_DATA_MODEL: { return entityDetail.tags || []; } @@ -463,6 +465,67 @@ export const getEntityOverview = ( return overview; } + case ExplorePageTabs.DASHBOARD_DATA_MODEL: { + const { owner, tags, href, service, displayName, dataModelType } = + entityDetail as DashboardDataModel; + const tier = getTierFromTableTags(tags || []); + + const overview = [ + { + name: i18next.t('label.owner'), + value: + getOwnerNameWithProfilePic(owner) || + i18next.t('label.no-entity', { + entity: i18next.t('label.owner'), + }), + url: getOwnerValue(owner as EntityReference), + isLink: owner?.name ? true : false, + visible: [DRAWER_NAVIGATION_OPTIONS.lineage], + }, + { + name: `${i18next.t('label.data-model')} ${i18next.t( + 'label.url-uppercase' + )}`, + value: displayName || NO_DATA, + url: href, + isLink: true, + isExternal: true, + visible: [ + DRAWER_NAVIGATION_OPTIONS.lineage, + DRAWER_NAVIGATION_OPTIONS.explore, + ], + }, + { + name: i18next.t('label.service'), + value: (service?.fullyQualifiedName as string) || NO_DATA, + url: getServiceDetailsPath( + service?.name as string, + ServiceCategory.DASHBOARD_SERVICES + ), + isExternal: false, + isLink: true, + visible: [DRAWER_NAVIGATION_OPTIONS.lineage], + }, + + { + name: i18next.t('label.tier'), + value: tier ? tier.split(FQN_SEPARATOR_CHAR)[1] : NO_DATA, + isLink: false, + isExternal: false, + visible: [DRAWER_NAVIGATION_OPTIONS.lineage], + }, + { + name: i18next.t('label.data-model-type'), + value: dataModelType, + isLink: false, + isExternal: false, + visible: [DRAWER_NAVIGATION_OPTIONS.lineage], + }, + ]; + + return overview; + } + default: return []; } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx index ff449f286fa..35d3f252dcd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TableUtils.tsx @@ -330,7 +330,6 @@ export const getEntityIcon = (indexType: string) => { case EntityType.CONTAINER: return ; - case SearchIndex.DASHBOARD_DATA_MODEL: case EntityType.DASHBOARD_DATA_MODEL: return ; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts index 130118bfb31..fa8951534af 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/TasksUtils.ts @@ -268,6 +268,9 @@ export const getBreadCrumbList = ( activeEntity, ]; } + case EntityType.DASHBOARD_DATA_MODEL: { + return [service(ServiceCategory.DASHBOARD_SERVICES), activeEntity]; + } default: return []; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntitySummaryPanelUtils.mock.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntitySummaryPanelUtils.mock.tsx index 016fae1305c..8c786e02088 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntitySummaryPanelUtils.mock.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntitySummaryPanelUtils.mock.tsx @@ -235,7 +235,7 @@ export const mockInvalidDataResponse = [ description: undefined, name: undefined, tags: undefined, - title: , + title: ---, type: undefined, }, ];