mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-30 11:56:01 +00:00
Feat 10854 : Added Version Page in DataModel entity (#11006)
* Added Version Page in DataModel entity * minor changes
This commit is contained in:
parent
9d11029ec8
commit
d17db66312
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright 2022 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 { Table, Typography } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import classNames from 'classnames';
|
||||
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
|
||||
import PageContainer from 'components/containers/PageContainer';
|
||||
import TagsViewer from 'components/Tag/TagsViewer/tags-viewer';
|
||||
import {
|
||||
ChangeDescription,
|
||||
Column,
|
||||
DashboardDataModel,
|
||||
} from 'generated/entity/data/dashboardDataModel';
|
||||
import { isUndefined } from 'lodash';
|
||||
import { ExtraInfo } from 'Models';
|
||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getEntityName } from 'utils/EntityUtils';
|
||||
import { FQN_SEPARATOR_CHAR } from '../../constants/char.constants';
|
||||
import { EntityField } from '../../constants/Feeds.constants';
|
||||
import { OwnerType } from '../../enums/user.enum';
|
||||
import { TagLabel } from '../../generated/type/tagLabel';
|
||||
import {
|
||||
getDescriptionDiff,
|
||||
getDiffByFieldName,
|
||||
getDiffValue,
|
||||
getTagsDiff,
|
||||
} from '../../utils/EntityVersionUtils';
|
||||
import { TagLabelWithStatus } from '../../utils/EntityVersionUtils.interface';
|
||||
import Description from '../common/description/Description';
|
||||
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
|
||||
import TabsPane from '../common/TabsPane/TabsPane';
|
||||
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
|
||||
import Loader from '../Loader/Loader';
|
||||
import { DataModelVersionProp } from './DataModelVersion.interface';
|
||||
|
||||
const DataModelVersion: FC<DataModelVersionProp> = ({
|
||||
version,
|
||||
currentVersionData,
|
||||
isVersionLoading,
|
||||
owner,
|
||||
tier,
|
||||
slashedDataModelName,
|
||||
versionList,
|
||||
deleted = false,
|
||||
backHandler,
|
||||
versionHandler,
|
||||
}: DataModelVersionProp) => {
|
||||
const { t } = useTranslation();
|
||||
const [changeDescription, setChangeDescription] = useState<ChangeDescription>(
|
||||
currentVersionData.changeDescription as ChangeDescription
|
||||
);
|
||||
const tabs = [
|
||||
{
|
||||
name: t('label.detail-plural'),
|
||||
isProtected: false,
|
||||
position: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const getDashboardDescription = () => {
|
||||
const descriptionDiff = getDiffByFieldName(
|
||||
EntityField.DESCRIPTION,
|
||||
changeDescription
|
||||
);
|
||||
const oldDescription =
|
||||
descriptionDiff?.added?.oldValue ??
|
||||
descriptionDiff?.deleted?.oldValue ??
|
||||
descriptionDiff?.updated?.oldValue;
|
||||
const newDescription =
|
||||
descriptionDiff?.added?.newValue ??
|
||||
descriptionDiff?.deleted?.newValue ??
|
||||
descriptionDiff?.updated?.newValue;
|
||||
|
||||
return getDescriptionDiff(
|
||||
oldDescription,
|
||||
newDescription,
|
||||
currentVersionData.description
|
||||
);
|
||||
};
|
||||
|
||||
const getExtraInfo = () => {
|
||||
const ownerDiff = getDiffByFieldName('owner', changeDescription);
|
||||
|
||||
const oldOwner = JSON.parse(
|
||||
ownerDiff?.added?.oldValue ??
|
||||
ownerDiff?.deleted?.oldValue ??
|
||||
ownerDiff?.updated?.oldValue ??
|
||||
'{}'
|
||||
);
|
||||
const newOwner = JSON.parse(
|
||||
ownerDiff?.added?.newValue ??
|
||||
ownerDiff?.deleted?.newValue ??
|
||||
ownerDiff?.updated?.newValue ??
|
||||
'{}'
|
||||
);
|
||||
const ownerPlaceHolder = owner?.name ?? owner?.displayName ?? '';
|
||||
|
||||
const tagsDiff = getDiffByFieldName('tags', changeDescription, true);
|
||||
const newTier = [
|
||||
...JSON.parse(
|
||||
tagsDiff?.added?.newValue ??
|
||||
tagsDiff?.deleted?.newValue ??
|
||||
tagsDiff?.updated?.newValue ??
|
||||
'[]'
|
||||
),
|
||||
].find((t) => (t?.tagFQN as string).startsWith('Tier'));
|
||||
|
||||
const oldTier = [
|
||||
...JSON.parse(
|
||||
tagsDiff?.added?.oldValue ??
|
||||
tagsDiff?.deleted?.oldValue ??
|
||||
tagsDiff?.updated?.oldValue ??
|
||||
'[]'
|
||||
),
|
||||
].find((t) => (t?.tagFQN as string).startsWith('Tier'));
|
||||
|
||||
const extraInfo: Array<ExtraInfo> = [
|
||||
{
|
||||
key: 'Owner',
|
||||
value:
|
||||
!isUndefined(ownerDiff?.added) ||
|
||||
!isUndefined(ownerDiff?.deleted) ||
|
||||
!isUndefined(ownerDiff?.updated)
|
||||
? getDiffValue(
|
||||
oldOwner?.displayName || oldOwner?.name || '',
|
||||
newOwner?.displayName || newOwner?.name || ''
|
||||
)
|
||||
: ownerPlaceHolder
|
||||
? getDiffValue(ownerPlaceHolder, ownerPlaceHolder)
|
||||
: '',
|
||||
profileName:
|
||||
newOwner?.type === OwnerType.USER ? newOwner?.name : undefined,
|
||||
},
|
||||
{
|
||||
key: 'Tier',
|
||||
value:
|
||||
!isUndefined(newTier) || !isUndefined(oldTier)
|
||||
? getDiffValue(
|
||||
oldTier?.tagFQN?.split(FQN_SEPARATOR_CHAR)[1] || '',
|
||||
newTier?.tagFQN?.split(FQN_SEPARATOR_CHAR)[1] || ''
|
||||
)
|
||||
: tier?.tagFQN
|
||||
? tier?.tagFQN.split(FQN_SEPARATOR_CHAR)[1]
|
||||
: '',
|
||||
},
|
||||
];
|
||||
|
||||
return extraInfo;
|
||||
};
|
||||
|
||||
const getTags = () => {
|
||||
const tagsDiff = getDiffByFieldName('tags', changeDescription, true);
|
||||
const oldTags: Array<TagLabel> = JSON.parse(
|
||||
tagsDiff?.added?.oldValue ??
|
||||
tagsDiff?.deleted?.oldValue ??
|
||||
tagsDiff?.updated?.oldValue ??
|
||||
'[]'
|
||||
);
|
||||
const newTags: Array<TagLabel> = JSON.parse(
|
||||
tagsDiff?.added?.newValue ??
|
||||
tagsDiff?.deleted?.newValue ??
|
||||
tagsDiff?.updated?.newValue ??
|
||||
'[]'
|
||||
);
|
||||
const flag: { [x: string]: boolean } = {};
|
||||
const uniqueTags: Array<TagLabelWithStatus> = [];
|
||||
|
||||
[
|
||||
...(getTagsDiff(oldTags, newTags) ?? []),
|
||||
...(currentVersionData.tags ?? []),
|
||||
].forEach((elem) => {
|
||||
if (!flag[elem.tagFQN as string]) {
|
||||
flag[elem.tagFQN as string] = true;
|
||||
uniqueTags.push(elem as TagLabelWithStatus);
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
...uniqueTags.map((t) =>
|
||||
t.tagFQN.startsWith('Tier')
|
||||
? { ...t, tagFQN: t.tagFQN.split(FQN_SEPARATOR_CHAR)[1] }
|
||||
: t
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setChangeDescription(
|
||||
currentVersionData.changeDescription as ChangeDescription
|
||||
);
|
||||
}, [currentVersionData]);
|
||||
|
||||
const tableColumn: ColumnsType<Column> = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: t('label.name'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 250,
|
||||
render: (_, record) => (
|
||||
<Typography.Text>{getEntityName(record)}</Typography.Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('label.type'),
|
||||
dataIndex: 'dataTypeDisplay',
|
||||
key: 'dataTypeDisplay',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: t('label.description'),
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
accessor: 'description',
|
||||
render: (text) =>
|
||||
text ? (
|
||||
<RichTextEditorPreviewer markdown={text} />
|
||||
) : (
|
||||
<span className="tw-no-description">
|
||||
{t('label.no-description')}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('label.tag-plural'),
|
||||
dataIndex: 'tags',
|
||||
key: 'tags',
|
||||
accessor: 'tags',
|
||||
width: 350,
|
||||
render: (tags: Column['tags']) => (
|
||||
<TagsViewer sizeCap={-1} tags={tags || []} />
|
||||
),
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<div
|
||||
className={classNames(
|
||||
'tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col tw-relative'
|
||||
)}
|
||||
data-testid="data-model-version-container">
|
||||
{isVersionLoading ? (
|
||||
<Loader />
|
||||
) : (
|
||||
<div
|
||||
className={classNames('version-data')}
|
||||
data-testid="version-data">
|
||||
<EntityPageInfo
|
||||
isVersionSelected
|
||||
deleted={deleted}
|
||||
entityName={
|
||||
currentVersionData.displayName ?? currentVersionData.name ?? ''
|
||||
}
|
||||
extraInfo={getExtraInfo()}
|
||||
followersList={[]}
|
||||
tags={getTags()}
|
||||
tier={{} as TagLabel}
|
||||
titleLinks={slashedDataModelName}
|
||||
version={Number(version)}
|
||||
versionHandler={backHandler}
|
||||
/>
|
||||
<div className="tw-mt-1 tw-flex tw-flex-col tw-flex-grow ">
|
||||
<TabsPane activeTab={1} className="tw-flex-initial" tabs={tabs} />
|
||||
<div className="tw-bg-white tw-flex-grow tw--mx-6 tw-px-7 tw-py-4">
|
||||
<div className="tw-grid tw-grid-cols-4 tw-gap-4 tw-w-full">
|
||||
<div className="tw-col-span-full">
|
||||
<Description
|
||||
isReadOnly
|
||||
description={getDashboardDescription()}
|
||||
/>
|
||||
</div>
|
||||
<div className="m-y-md tw-col-span-full">
|
||||
<Table
|
||||
bordered
|
||||
columns={tableColumn}
|
||||
data-testid="schema-table"
|
||||
dataSource={
|
||||
(currentVersionData as DashboardDataModel)?.columns
|
||||
}
|
||||
pagination={false}
|
||||
rowKey="id"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<EntityVersionTimeLine
|
||||
show
|
||||
currentVersion={version}
|
||||
versionHandler={versionHandler}
|
||||
versionList={versionList}
|
||||
onBack={backHandler}
|
||||
/>
|
||||
</div>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataModelVersion;
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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 { VersionData } from 'pages/EntityVersionPage/EntityVersionPage.component';
|
||||
import { EntityHistory } from '../../generated/type/entityHistory';
|
||||
import { TagLabel } from '../../generated/type/tagLabel';
|
||||
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
|
||||
|
||||
export interface DataModelVersionProp {
|
||||
version: string;
|
||||
currentVersionData: VersionData;
|
||||
isVersionLoading: boolean;
|
||||
owner: DashboardDataModel['owner'];
|
||||
tier: TagLabel;
|
||||
slashedDataModelName: TitleBreadcrumbProps['titleLinks'];
|
||||
topicFQN: string;
|
||||
versionList: EntityHistory;
|
||||
deleted?: boolean;
|
||||
backHandler: () => void;
|
||||
versionHandler: (v: string) => void;
|
||||
}
|
@ -21,7 +21,7 @@ import EntityLineageComponent from 'components/EntityLineage/EntityLineage.compo
|
||||
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 { getServiceDetailsPath, getVersionPath } from 'constants/constants';
|
||||
import { EntityField } from 'constants/Feeds.constants';
|
||||
import { observerOptions } from 'constants/Mydata.constants';
|
||||
import { CSMode } from 'enums/codemirror.enum';
|
||||
@ -30,10 +30,12 @@ import { ServiceCategory } from 'enums/service.enum';
|
||||
import { OwnerType } from 'enums/user.enum';
|
||||
import { Paging } from 'generated/type/paging';
|
||||
import { useInfiniteScroll } from 'hooks/useInfiniteScroll';
|
||||
import { toString } from 'lodash';
|
||||
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 { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
getCountBadge,
|
||||
getCurrentUserId,
|
||||
@ -74,6 +76,7 @@ const DataModelDetails = ({
|
||||
handleFeedFilterChange,
|
||||
}: DataModelDetailsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const history = useHistory();
|
||||
const [elementRef, isInView] = useInfiniteScroll(observerOptions);
|
||||
const [isEditDescription, setIsEditDescription] = useState<boolean>(false);
|
||||
const [threadLink, setThreadLink] = useState<string>('');
|
||||
@ -181,6 +184,16 @@ const DataModelDetails = ({
|
||||
},
|
||||
];
|
||||
|
||||
const versionHandler = () => {
|
||||
history.push(
|
||||
getVersionPath(
|
||||
EntityType.DASHBOARD_DATA_MODEL,
|
||||
dashboardDataModelFQN,
|
||||
toString(version)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const onThreadLinkSelect = (link: string) => {
|
||||
setThreadLink(link);
|
||||
};
|
||||
@ -241,6 +254,7 @@ const DataModelDetails = ({
|
||||
updateOwner={hasEditOwnerPermission ? handleUpdateOwner : undefined}
|
||||
updateTier={hasEditTierPermission ? handleUpdateTier : undefined}
|
||||
version={version}
|
||||
versionHandler={versionHandler}
|
||||
onThreadLinkSelect={onThreadLinkSelect}
|
||||
/>
|
||||
<Tabs
|
||||
@ -302,22 +316,20 @@ const DataModelDetails = ({
|
||||
)}
|
||||
</span>
|
||||
}>
|
||||
<Card>
|
||||
<Row justify="center">
|
||||
<Col span={18}>
|
||||
<div id="activityfeed">
|
||||
<ActivityFeedList
|
||||
isEntityFeed
|
||||
withSidePanel
|
||||
deletePostHandler={deletePostHandler}
|
||||
entityName={entityName}
|
||||
feedList={entityThread}
|
||||
isFeedLoading={isEntityThreadLoading}
|
||||
postFeedHandler={postFeedHandler}
|
||||
updateThreadHandler={updateThreadHandler}
|
||||
onFeedFiltersUpdate={handleFeedFilterChange}
|
||||
/>
|
||||
</div>
|
||||
<Card className="h-min-full">
|
||||
<Row>
|
||||
<Col data-testid="activityfeed" offset={3} span={18}>
|
||||
<ActivityFeedList
|
||||
isEntityFeed
|
||||
withSidePanel
|
||||
deletePostHandler={deletePostHandler}
|
||||
entityName={entityName}
|
||||
feedList={entityThread}
|
||||
isFeedLoading={isEntityThreadLoading}
|
||||
postFeedHandler={postFeedHandler}
|
||||
updateThreadHandler={updateThreadHandler}
|
||||
onFeedFiltersUpdate={handleFeedFilterChange}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
{loader}
|
||||
|
@ -61,7 +61,7 @@ const ModelTab = ({
|
||||
}, [fetchTagsAndGlossaryTerms]);
|
||||
|
||||
const handleFieldTagsChange = useCallback(
|
||||
async (selectedTags: EntityTags[] = [], selectedColumn: Column) => {
|
||||
async (selectedTags: EntityTags[] = [], columnName: string) => {
|
||||
const newSelectedTags: TagOption[] = selectedTags.map((tag) => ({
|
||||
fqn: tag.tagFQN,
|
||||
source: tag.source,
|
||||
@ -69,11 +69,7 @@ const ModelTab = ({
|
||||
|
||||
const dataModelData = cloneDeep(data);
|
||||
|
||||
updateDataModelColumnTags(
|
||||
dataModelData,
|
||||
editContainerColumnTags?.name ?? selectedColumn.name,
|
||||
newSelectedTags
|
||||
);
|
||||
updateDataModelColumnTags(dataModelData, columnName, newSelectedTags);
|
||||
|
||||
await onUpdate(dataModelData);
|
||||
|
||||
@ -172,7 +168,7 @@ const ModelTab = ({
|
||||
type="label"
|
||||
onCancel={() => setEditContainerColumnTags(undefined)}
|
||||
onSelectionChange={(tags) =>
|
||||
handleFieldTagsChange(tags, record)
|
||||
handleFieldTagsChange(tags, record.name)
|
||||
}
|
||||
/>
|
||||
</Space>
|
||||
@ -238,7 +234,7 @@ const ModelTab = ({
|
||||
bordered
|
||||
className="p-t-xs"
|
||||
columns={tableColumn}
|
||||
data-testid="charts-table"
|
||||
data-testid="data-model-column-table"
|
||||
dataSource={data}
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
Position,
|
||||
useUpdateNodeInternals,
|
||||
} from 'reactflow';
|
||||
import { getEntityName } from 'utils/EntityUtils';
|
||||
import { EntityLineageNodeType } from '../../enums/entity.enum';
|
||||
import { getNodeRemoveButton } from '../../utils/EntityLineageUtils';
|
||||
import { getConstraintIcon } from '../../utils/TableUtils';
|
||||
@ -175,7 +176,7 @@ const CustomNode = (props: NodeProps) => {
|
||||
column.fullyQualifiedName
|
||||
)}
|
||||
{getConstraintIcon(column.constraint, 'tw-')}
|
||||
<p className="m-0">{column.name}</p>
|
||||
<p className="m-0">{getEntityName(column)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -56,7 +56,10 @@ import {
|
||||
getEntityMissingError,
|
||||
getFeedCounts,
|
||||
} from 'utils/CommonUtils';
|
||||
import { getDataModelsDetailPath } from 'utils/DataModelsUtils';
|
||||
import {
|
||||
getDataModelsDetailPath,
|
||||
getSortedDataModelColumnTags,
|
||||
} from 'utils/DataModelsUtils';
|
||||
import { getEntityFeedLink } from 'utils/EntityUtils';
|
||||
import { deletePost, updateThreadData } from 'utils/FeedUtils';
|
||||
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
||||
@ -412,7 +415,7 @@ const DataModelsPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateDataModel = async (
|
||||
const handleColumnUpdateDataModel = async (
|
||||
updatedDataModel: DashboardDataModel['columns']
|
||||
) => {
|
||||
try {
|
||||
@ -423,9 +426,10 @@ const DataModelsPage = () => {
|
||||
|
||||
setDataModelData((prev) => ({
|
||||
...(prev as DashboardDataModel),
|
||||
columns: newColumns,
|
||||
columns: getSortedDataModelColumnTags(newColumns),
|
||||
version,
|
||||
}));
|
||||
getEntityFeedCount();
|
||||
} catch (error) {
|
||||
showErrorToast(error as AxiosError);
|
||||
}
|
||||
@ -435,7 +439,7 @@ const DataModelsPage = () => {
|
||||
if (tab === DATA_MODELS_DETAILS_TABS.ACTIVITY) {
|
||||
getFeedData();
|
||||
}
|
||||
}, [tab, dashboardDataModelFQN]);
|
||||
}, [tab, feedCount, dashboardDataModelFQN]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasViewPermission) {
|
||||
@ -482,7 +486,7 @@ const DataModelsPage = () => {
|
||||
handleFollowDataModel={handleFollowDataModel}
|
||||
handleRemoveTier={handleRemoveTier}
|
||||
handleTabChange={handleTabChange}
|
||||
handleUpdateDataModel={handleUpdateDataModel}
|
||||
handleUpdateDataModel={handleColumnUpdateDataModel}
|
||||
handleUpdateDescription={handleUpdateDescription}
|
||||
handleUpdateOwner={handleUpdateOwner}
|
||||
handleUpdateTags={handleUpdateTags}
|
||||
|
@ -15,12 +15,14 @@ import { AxiosError } from 'axios';
|
||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||
import ContainerVersion from 'components/ContainerVersion/ContainerVersion.component';
|
||||
import DashboardVersion from 'components/DashboardVersion/DashboardVersion.component';
|
||||
import DataModelVersion from 'components/DataModelVersion/DataModelVersion.component';
|
||||
import DatasetVersion from 'components/DatasetVersion/DatasetVersion.component';
|
||||
import Loader from 'components/Loader/Loader';
|
||||
import MlModelVersion from 'components/MlModelVersion/MlModelVersion.component';
|
||||
import PipelineVersion from 'components/PipelineVersion/PipelineVersion.component';
|
||||
import TopicVersion from 'components/TopicVersion/TopicVersion.component';
|
||||
import { Container } from 'generated/entity/data/container';
|
||||
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
|
||||
import { Mlmodel } from 'generated/entity/data/mlmodel';
|
||||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -30,6 +32,11 @@ import {
|
||||
getDashboardVersion,
|
||||
getDashboardVersions,
|
||||
} from 'rest/dashboardAPI';
|
||||
import {
|
||||
getDataModelDetailsByFQN,
|
||||
getDataModelVersion,
|
||||
getDataModelVersionsList,
|
||||
} from 'rest/dataModelsAPI';
|
||||
import {
|
||||
getMlModelByFQN,
|
||||
getMlModelVersion,
|
||||
@ -62,6 +69,7 @@ import {
|
||||
getDashboardDetailsPath,
|
||||
getDatabaseDetailsPath,
|
||||
getDatabaseSchemaDetailsPath,
|
||||
getDataModelDetailsPath,
|
||||
getMlModelDetailsPath,
|
||||
getPipelineDetailsPath,
|
||||
getServiceDetailsPath,
|
||||
@ -81,7 +89,9 @@ import {
|
||||
getPartialNameFromFQN,
|
||||
getPartialNameFromTableFQN,
|
||||
} from '../../utils/CommonUtils';
|
||||
import { defaultFields as DataModelFields } from '../../utils/DataModelsUtils';
|
||||
import { defaultFields as MlModelFields } from '../../utils/MlModelDetailsUtils';
|
||||
|
||||
import { serviceTypeLogo } from '../../utils/ServiceUtils';
|
||||
import { getTierTags } from '../../utils/TableUtils';
|
||||
import { showErrorToast } from '../../utils/ToastUtils';
|
||||
@ -92,7 +102,8 @@ export type VersionData =
|
||||
| Dashboard
|
||||
| Pipeline
|
||||
| Mlmodel
|
||||
| Container;
|
||||
| Container
|
||||
| DashboardDataModel;
|
||||
|
||||
const EntityVersionPage: FunctionComponent = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -109,6 +120,7 @@ const EntityVersionPage: FunctionComponent = () => {
|
||||
string,
|
||||
string
|
||||
>;
|
||||
|
||||
const [isLoading, setIsloading] = useState<boolean>(false);
|
||||
const [versionList, setVersionList] = useState<EntityHistory>(
|
||||
{} as EntityHistory
|
||||
@ -148,6 +160,10 @@ const EntityVersionPage: FunctionComponent = () => {
|
||||
case EntityType.CONTAINER:
|
||||
history.push(getContainerDetailPath(entityFQN));
|
||||
|
||||
break;
|
||||
case EntityType.DASHBOARD_DATA_MODEL:
|
||||
history.push(getDataModelDetailsPath(entityFQN));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -531,6 +547,57 @@ const EntityVersionPage: FunctionComponent = () => {
|
||||
|
||||
break;
|
||||
}
|
||||
case EntityType.DASHBOARD_DATA_MODEL: {
|
||||
getDataModelDetailsByFQN(entityFQN, DataModelFields)
|
||||
.then((res) => {
|
||||
const { id, owner, tags = [], service, serviceType } = res;
|
||||
const serviceName = service?.name ?? '';
|
||||
setEntityState(tags, owner, res, [
|
||||
{
|
||||
name: serviceName,
|
||||
url: serviceName
|
||||
? getServiceDetailsPath(
|
||||
serviceName,
|
||||
ServiceCategory.DASHBOARD_SERVICES
|
||||
)
|
||||
: '',
|
||||
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
|
||||
},
|
||||
{
|
||||
name: getEntityName(res),
|
||||
url: '',
|
||||
activeTitle: true,
|
||||
},
|
||||
]);
|
||||
|
||||
getDataModelVersionsList(id ?? '')
|
||||
.then((vres) => {
|
||||
setVersionList(vres);
|
||||
setIsloading(false);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.entity-fetch-version-error', {
|
||||
entity: entityFQN,
|
||||
version: '',
|
||||
})
|
||||
);
|
||||
});
|
||||
})
|
||||
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.entity-fetch-version-error', {
|
||||
entity: entityFQN,
|
||||
version: '',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -896,6 +963,58 @@ const EntityVersionPage: FunctionComponent = () => {
|
||||
break;
|
||||
}
|
||||
|
||||
case EntityType.DASHBOARD_DATA_MODEL: {
|
||||
getDataModelDetailsByFQN(entityFQN, [])
|
||||
.then((res) => {
|
||||
const { id, service, serviceType } = res;
|
||||
getDataModelVersion(id ?? '', version)
|
||||
.then((vRes) => {
|
||||
const { owner, tags = [] } = vRes;
|
||||
const serviceName = service?.name ?? '';
|
||||
setEntityState(tags, owner, vRes, [
|
||||
{
|
||||
name: serviceName,
|
||||
url: serviceName
|
||||
? getServiceDetailsPath(
|
||||
serviceName,
|
||||
ServiceCategory.DASHBOARD_SERVICES
|
||||
)
|
||||
: '',
|
||||
imgSrc: serviceType
|
||||
? serviceTypeLogo(serviceType)
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
name: getEntityName(res),
|
||||
url: '',
|
||||
activeTitle: true,
|
||||
},
|
||||
]);
|
||||
setIsVersionLoading(false);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.entity-fetch-version-error', {
|
||||
entity: entityFQN,
|
||||
version: version,
|
||||
})
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
showErrorToast(
|
||||
err,
|
||||
t('server.entity-fetch-version-error', {
|
||||
entity: entityFQN,
|
||||
version: version,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1009,6 +1128,24 @@ const EntityVersionPage: FunctionComponent = () => {
|
||||
);
|
||||
}
|
||||
|
||||
case EntityType.DASHBOARD_DATA_MODEL: {
|
||||
return (
|
||||
<DataModelVersion
|
||||
backHandler={backHandler}
|
||||
currentVersionData={currentVersionData}
|
||||
deleted={currentVersionData.deleted}
|
||||
isVersionLoading={isVersionLoading}
|
||||
owner={owner}
|
||||
slashedDataModelName={slashedEntityName}
|
||||
tier={tier as TagLabel}
|
||||
topicFQN={entityFQN}
|
||||
version={version}
|
||||
versionHandler={versionHandler}
|
||||
versionList={versionList}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
@ -46,6 +46,10 @@ jest.mock('components/ContainerVersion/ContainerVersion.component', () => {
|
||||
return jest.fn().mockReturnValue(<div>ContainerVersion component</div>);
|
||||
});
|
||||
|
||||
jest.mock('components/DataModelVersion/DataModelVersion.component', () => {
|
||||
return jest.fn().mockReturnValue(<div>DataModelVersion component</div>);
|
||||
});
|
||||
|
||||
jest.mock('rest/dashboardAPI', () => ({
|
||||
getDashboardByFqn: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
getDashboardVersion: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
@ -79,6 +83,16 @@ jest.mock('rest/objectStoreAPI', () => ({
|
||||
getContainerVersions: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
jest.mock('rest/dataModelsAPI', () => ({
|
||||
getDataModelDetailsByFQN: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
getDataModelVersion: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
getDataModelVersionsList: jest
|
||||
.fn()
|
||||
.mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
describe('Test EntityVersionPage component', () => {
|
||||
it('Checks if the DatasetVersion component renderst if respective data pass', async () => {
|
||||
await act(async () => {
|
||||
@ -197,4 +211,25 @@ describe('Test EntityVersionPage component', () => {
|
||||
expect(ContainerVersion).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should render the DataModel Version Component', async () => {
|
||||
mockParams = {
|
||||
entityType: 'dashboardDataModel',
|
||||
version: '0.1',
|
||||
entityFQN: 'data_model.sales',
|
||||
};
|
||||
|
||||
await act(async () => {
|
||||
const { container } = render(<EntityVersionPage />, {
|
||||
wrapper: MemoryRouter,
|
||||
});
|
||||
|
||||
const ContainerVersion = await findByText(
|
||||
container,
|
||||
/DataModelVersion component/i
|
||||
);
|
||||
|
||||
expect(ContainerVersion).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -13,6 +13,7 @@
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { Operation } from 'fast-json-patch';
|
||||
import { DashboardDataModel } from 'generated/entity/data/dashboardDataModel';
|
||||
import { EntityHistory } from 'generated/type/entityHistory';
|
||||
import { EntityReference } from 'generated/type/entityReference';
|
||||
import { getURLWithQueryFields } from 'utils/APIUtils';
|
||||
import APIClient from './index';
|
||||
@ -93,3 +94,19 @@ export const removeDataModelFollower = async (id: string, userId: string) => {
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getDataModelVersionsList = async (id: string) => {
|
||||
const url = `${URL}/${id}/versions`;
|
||||
|
||||
const response = await APIClient.get<EntityHistory>(url);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getDataModelVersion = async (id: string, version: string) => {
|
||||
const url = `${URL}/${id}/versions/${version}`;
|
||||
|
||||
const response = await APIClient.get<DashboardDataModel>(url);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
@ -279,7 +279,8 @@
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 24px 16px 0px 16px;
|
||||
padding: 16px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.description-text {
|
||||
|
@ -20,6 +20,7 @@ import { Column } from 'generated/entity/data/dashboardDataModel';
|
||||
import { LabelType, State, TagLabel } from 'generated/type/tagLabel';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { EntityTags, TagOption } from 'Models';
|
||||
import { sortTagsCaseInsensitive } from './CommonUtils';
|
||||
|
||||
export const getDataModelsDetailPath = (dataModelFQN: string, tab?: string) => {
|
||||
let path = tab
|
||||
@ -116,3 +117,9 @@ export const updateDataModelColumnTags = (
|
||||
|
||||
export const defaultFields = `${TabSpecificField.TAGS}, ${TabSpecificField.OWNER},
|
||||
${TabSpecificField.FOLLOWERS}`;
|
||||
|
||||
export const getSortedDataModelColumnTags = (column: Column[]): Column[] =>
|
||||
column.map((item) => ({
|
||||
...item,
|
||||
tags: sortTagsCaseInsensitive(item.tags ?? []),
|
||||
}));
|
||||
|
Loading…
x
Reference in New Issue
Block a user