UI: Adding versioning for topic entity. (#1884)

* UI: Adding versioning for topic entity.

* Adding versions button

* Adding topicversion component.

* Adding diff for topic tags and tier.

* Adding license for new file.

* Removed Fragment .

* Moving state dispatch to method.
This commit is contained in:
Sachin Chaurasiya 2021-12-27 15:47:04 +05:30 committed by GitHub
parent c925168f65
commit 0f28114b47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 748 additions and 169 deletions

View File

@ -16,6 +16,22 @@ import { Topic } from 'Models';
import { getURLWithQueryFields } from '../utils/APIUtils';
import APIClient from './index';
export const getTopicVersions: Function = (
id: string
): Promise<AxiosResponse> => {
const url = `/topics/${id}/versions`;
return APIClient.get(url);
};
export const getTopicVersion: Function = (
id: string,
version: string
): Promise<AxiosResponse> => {
const url = `/topics/${id}/versions/${version}`;
return APIClient.get(url);
};
export const getTopics: Function = (
serviceName: string,
paging: string,

View File

@ -197,9 +197,9 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
});
};
formatColumnData(colList);
formatColumnData(colList ?? []);
return colList;
return colList ?? [];
} else if (
isEndsWithField(
columnsDiff?.added?.name ??
@ -244,9 +244,9 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
});
};
formatColumnData(colList);
formatColumnData(colList ?? []);
return colList;
return colList ?? [];
} else {
const columnsDiff = getDiffByFieldName(
'columns',
@ -279,7 +279,7 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
}
});
};
formatColumnData(colList);
formatColumnData(colList ?? []);
});
}
if (columnsDiff.deleted) {
@ -302,10 +302,10 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
name: getDescriptionDiff(col.name, undefined, col.name),
}));
} else {
return colList;
return colList ?? [];
}
return [...newColumns, ...colList];
return [...newColumns, ...(colList ?? [])];
}
};
@ -341,7 +341,7 @@ const DatasetVersion: React.FC<DatasetVersionProp> = ({
<div className={classNames('version-data')}>
<EntityPageInfo
isVersionSelected
entityName={currentVersionData.name}
entityName={currentVersionData.name ?? ''}
extraInfo={getExtraInfo()}
followersList={[]}
tags={getTableTags(currentVersionData.columns || [])}

View File

@ -14,11 +14,12 @@
import { Table } from '../../generated/entity/data/table';
import { EntityHistory } from '../../generated/type/entityHistory';
import { TagLabel } from '../../generated/type/tagLabel';
import { VersionData } from '../../pages/EntityVersionPage/EntityVersionPage.component';
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
export interface DatasetVersionProp {
version: string;
currentVersionData: Table;
currentVersionData: VersionData;
isVersionLoading: boolean;
owner: Table['owner'];
tier: TagLabel;

View File

@ -54,6 +54,8 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
unfollowTopicHandler,
descriptionUpdateHandler,
tagUpdateHandler,
version,
versionHandler,
}: TopicDetailsProps) => {
const { isAuthDisabled } = useAuth();
const [isEdit, setIsEdit] = useState(false);
@ -286,6 +288,8 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
tagsHandler={onTagUpdate}
tier={tier ?? ''}
titleLinks={slashedTopicName}
version={version}
versionHandler={versionHandler}
/>
<div className="tw-mt-4 tw-flex tw-flex-col tw-flex-grow">
<TabsPane

View File

@ -19,6 +19,7 @@ import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrum
export interface TopicDetailsProps {
tagList: Array<string>;
version?: string;
schemaText: string;
schemaType: string;
partitions: number;
@ -42,4 +43,5 @@ export interface TopicDetailsProps {
settingsUpdateHandler: (updatedTopic: Topic) => Promise<void>;
descriptionUpdateHandler: (updatedTopic: Topic) => void;
tagUpdateHandler: (updatedTopic: Topic) => void;
versionHandler: () => void;
}

View File

@ -0,0 +1,318 @@
/*
* 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 classNames from 'classnames';
import { isUndefined } from 'lodash';
import { ExtraInfo } from 'Models';
import React, { FC, useEffect, useState } from 'react';
import { ChangeDescription } from '../../generated/entity/data/topic';
import { TagLabel } from '../../generated/type/tagLabel';
import {
getDescriptionDiff,
getDiffByFieldName,
getDiffValue,
getTagsDiff,
} from '../../utils/EntityVersionUtils';
import { bytesToSize } from '../../utils/StringsUtils';
import { getOwnerFromId } from '../../utils/TableUtils';
import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import TabsPane from '../common/TabsPane/TabsPane';
import PageContainer from '../containers/PageContainer';
import EntityVersionTimeLine from '../EntityVersionTimeLine/EntityVersionTimeLine';
import Loader from '../Loader/Loader';
import SchemaEditor from '../schema-editor/SchemaEditor';
import { TopicVersionProp } from './TopicVersion.interface';
const TopicVersion: FC<TopicVersionProp> = ({
version,
currentVersionData,
isVersionLoading,
owner,
tier,
slashedTopicName,
versionList,
backHandler,
versionHandler,
}: TopicVersionProp) => {
const [changeDescription, setChangeDescription] = useState<ChangeDescription>(
currentVersionData.changeDescription as ChangeDescription
);
const tabs = [
{
name: 'Schema',
icon: {
alt: 'schema',
name: 'icon-schema',
title: 'Schema',
selectedName: 'icon-schemacolor',
},
isProtected: false,
position: 1,
},
];
const getConfigDetails = () => {
return [
{
key: 'Partitions',
value: `${currentVersionData.partitions ?? '--'} partitions`,
},
{
key: 'Replication Factor',
value: `${
currentVersionData.replicationFactor ?? '--'
} replication factor`,
},
{
key: 'Retention Size',
value: `${bytesToSize(
currentVersionData.retentionSize ?? 0
)} retention size`,
},
{
key: 'Clean-up Policies',
value: `${currentVersionData?.cleanupPolicies?.join(
', '
)} clean-up policies`,
},
{
key: 'Max Message Size',
value: `${bytesToSize(
currentVersionData.maximumMessageSize ?? 0
)} maximum size`,
},
];
};
const getTableDescription = () => {
const descriptionDiff = getDiffByFieldName(
'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 = getOwnerFromId(
JSON.parse(
ownerDiff?.added?.oldValue ??
ownerDiff?.deleted?.oldValue ??
ownerDiff?.updated?.oldValue ??
'{}'
)?.id
);
const newOwner = getOwnerFromId(
JSON.parse(
ownerDiff?.added?.newValue ??
ownerDiff?.deleted?.newValue ??
ownerDiff?.updated?.newValue ??
'{}'
)?.id
);
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)
: '',
},
{
key: 'Tier',
value:
!isUndefined(newTier) || !isUndefined(oldTier)
? getDiffValue(
oldTier?.tagFQN?.split('.')[1] || '',
newTier?.tagFQN?.split('.')[1] || ''
)
: tier?.tagFQN
? tier?.tagFQN.split('.')[1]
: '',
},
...getConfigDetails(),
];
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<TagLabel & { added: boolean; removed: boolean }> =
[];
[
...(getTagsDiff(oldTags, newTags) ?? []),
...(currentVersionData.tags ?? []),
].forEach((elem: TagLabel & { added: boolean; removed: boolean }) => {
if (!flag[elem.tagFQN as string]) {
flag[elem.tagFQN as string] = true;
uniqueTags.push(elem);
}
});
return [
...uniqueTags.map((t) =>
t.tagFQN.startsWith('Tier')
? { ...t, tagFQN: t.tagFQN.split('.')[1] }
: t
),
];
};
const getInfoBadge = (infos: Array<Record<string, string | number>>) => {
return (
<div className="tw-flex tw-justify-between">
<div className="tw-flex tw-gap-3">
{infos.map((info, index) => (
<div className="tw-mt-4" key={index}>
<span className="tw-py-1.5 tw-px-2 tw-rounded-l tw-bg-tag ">
{info.key}
</span>
<span className="tw-py-1.5 tw-px-2 tw-bg-primary-lite tw-font-normal tw-rounded-r">
{info.value}
</span>
</div>
))}
</div>
<div />
</div>
);
};
useEffect(() => {
setChangeDescription(
currentVersionData.changeDescription as ChangeDescription
);
}, [currentVersionData]);
return (
<PageContainer>
<div
className={classNames(
'tw-px-4 tw-w-full tw-h-full tw-flex tw-flex-col tw-relative'
)}>
{isVersionLoading ? (
<Loader />
) : (
<div className={classNames('version-data')}>
<EntityPageInfo
isVersionSelected
entityName={currentVersionData.name ?? ''}
extraInfo={getExtraInfo()}
followersList={[]}
tags={getTags()}
tier={{} as TagLabel}
titleLinks={slashedTopicName}
version={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-1">
<div className="tw-grid tw-grid-cols-4 tw-gap-4 tw-w-full tw-mt-4 ">
<div className="tw-col-span-full">
<Description
isReadOnly
description={getTableDescription()}
/>
</div>
<div className="tw-col-span-full">
{getInfoBadge([
{
key: 'Schema',
value: currentVersionData.schemaType ?? '',
},
])}
<div className="tw-my-4 tw-border tw-border-main tw-rounded-md tw-py-4">
<SchemaEditor
value={currentVersionData.schemaText ?? '{}'}
/>
</div>
</div>
</div>
</div>
</div>
</div>
)}
<EntityVersionTimeLine
show
currentVersion={version}
versionHandler={versionHandler}
versionList={versionList}
onBack={backHandler}
/>
</div>
</PageContainer>
);
};
export default TopicVersion;

View File

@ -0,0 +1,31 @@
/*
* 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 { Topic } from '../../generated/entity/data/topic';
import { EntityHistory } from '../../generated/type/entityHistory';
import { TagLabel } from '../../generated/type/tagLabel';
import { VersionData } from '../../pages/EntityVersionPage/EntityVersionPage.component';
import { TitleBreadcrumbProps } from '../common/title-breadcrumb/title-breadcrumb.interface';
export interface TopicVersionProp {
version: string;
currentVersionData: VersionData;
isVersionLoading: boolean;
owner: Topic['owner'];
tier: TagLabel;
slashedTopicName: TitleBreadcrumbProps['titleLinks'];
topicFQN: string;
versionList: EntityHistory;
backHandler: () => void;
versionHandler: (v: string) => void;
}

View File

@ -12,7 +12,7 @@
*/
import classNames from 'classnames';
import { isUndefined } from 'lodash';
import { isEmpty, isUndefined } from 'lodash';
import { EntityTags, ExtraInfo, TableDetail } from 'Models';
import React, { useEffect, useState } from 'react';
import { FOLLOWERS_VIEW_CAP, LIST_SIZE } from '../../../constants/constants';
@ -246,7 +246,7 @@ const EntityPageInfo = ({
</div>
</div>
</div>
<div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7">
<div className="tw-flex tw-gap-1 tw-mb-2 tw-mt-1 tw-ml-7 tw-flex-wrap">
{extraInfo.map((info, index) => (
<span className="tw-flex" key={index}>
{getInfoElements(info)}
@ -261,7 +261,7 @@ const EntityPageInfo = ({
<div className="tw-flex tw-flex-wrap tw-pt-1 tw-ml-7 tw-group">
{(!isEditable || !isTagEditable) && (
<>
{(tags.length > 0 || tier) && (
{(tags.length > 0 || !isEmpty(tier)) && (
<SVGIcons
alt="icon-tag"
className="tw-mx-1"
@ -279,7 +279,16 @@ const EntityPageInfo = ({
{tags.length > 0 && (
<>
{tags.slice(0, LIST_SIZE).map((tag, index) => (
<Tags key={index} startWith="#" tag={tag} type="label" />
<Tags
className={classNames(
{ 'diff-added tw-mx-1': tag?.added },
{ 'diff-removed': tag?.removed }
)}
key={index}
startWith="#"
tag={tag}
type="label"
/>
))}
{tags.slice(LIST_SIZE).length > 0 && (
@ -288,7 +297,15 @@ const EntityPageInfo = ({
<>
{tags.slice(LIST_SIZE).map((tag, index) => (
<p className="tw-text-left" key={index}>
<Tags startWith="#" tag={tag} type="label" />
<Tags
className={classNames(
{ 'diff-added tw-mx-1': tag?.added },
{ 'diff-removed': tag?.removed }
)}
startWith="#"
tag={tag}
type="label"
/>
</p>
))}
</>

View File

@ -44,6 +44,8 @@ const PLACEHOLDER_ROUTE_SEARCHQUERY = ':searchQuery';
const PLACEHOLDER_ROUTE_TAB = ':tab';
const PLACEHOLDER_ROUTE_TEAM = ':team';
const PLAEHOLDER_ROUTE_VERSION = ':version';
const PLACEHOLDER_ROUTE_ENTITY_TYPE = ':entityType';
const PLACEHOLDER_ROUTE_ENTITY_FQN = ':entityFQN';
export const pagingObject = { after: '', before: '' };
@ -135,7 +137,7 @@ export const ROUTES = {
SIGNIN: '/signin',
DATASET_DETAILS: `/dataset/${PLACEHOLDER_ROUTE_DATASET_FQN}`,
DATASET_DETAILS_WITH_TAB: `/dataset/${PLACEHOLDER_ROUTE_DATASET_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
DATASET_VERSION: `/dataset/${PLACEHOLDER_ROUTE_DATASET_FQN}/versions/${PLAEHOLDER_ROUTE_VERSION}`,
ENTITY_VERSION: `/${PLACEHOLDER_ROUTE_ENTITY_TYPE}/${PLACEHOLDER_ROUTE_ENTITY_FQN}/versions/${PLAEHOLDER_ROUTE_VERSION}`,
TOPIC_DETAILS: `/topic/${PLACEHOLDER_ROUTE_TOPIC_FQN}`,
TOPIC_DETAILS_WITH_TAB: `/topic/${PLACEHOLDER_ROUTE_TOPIC_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
DASHBOARD_DETAILS: `/dashboard/${PLACEHOLDER_ROUTE_DASHBOARD_FQN}`,
@ -162,10 +164,15 @@ export const getDatasetDetailsPath = (
return `${path}${columnName ? `.${columnName}` : ''}`;
};
export const getDatasetVersionPath = (datasetFQN: string, version: string) => {
let path = ROUTES.DATASET_VERSION;
export const getVersionPath = (
entityType: string,
fqn: string,
version: string
) => {
let path = ROUTES.ENTITY_VERSION;
path = path
.replace(PLACEHOLDER_ROUTE_DATASET_FQN, datasetFQN)
.replace(PLACEHOLDER_ROUTE_ENTITY_TYPE, entityType)
.replace(PLACEHOLDER_ROUTE_ENTITY_FQN, fqn)
.replace(PLAEHOLDER_ROUTE_VERSION, version);
return path;

View File

@ -31,7 +31,7 @@ import Loader from '../../components/Loader/Loader';
import {
getDatabaseDetailsPath,
getDatasetTabPath,
getDatasetVersionPath,
getVersionPath,
getServiceDetailsPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum';
@ -100,7 +100,6 @@ const DatasetDetailsPage: FunctionComponent = () => {
{} as TypeUsedToReturnUsageDetailsOfAnEntity
);
const [currentVersion, setCurrentVersion] = useState<string>();
const [, setPreviousVersion] = useState<string>();
const [isNodeLoading, setNodeLoading] = useState<LoadingNodeState>({
id: undefined,
state: false,
@ -138,9 +137,8 @@ const DatasetDetailsPage: FunctionComponent = () => {
const descriptionUpdateHandler = (updatedTable: Table) => {
saveUpdatedTableData(updatedTable).then((res: AxiosResponse) => {
const { description, version, changeDescription } = res.data;
const { description, version } = res.data;
setCurrentVersion(version);
setPreviousVersion(changeDescription.previousVersion);
setTableDetails(res.data);
setDescription(description);
});
@ -148,9 +146,8 @@ const DatasetDetailsPage: FunctionComponent = () => {
const columnsUpdateHandler = (updatedTable: Table) => {
saveUpdatedTableData(updatedTable).then((res: AxiosResponse) => {
const { columns, version, changeDescription } = res.data;
const { columns, version } = res.data;
setCurrentVersion(version);
setPreviousVersion(changeDescription.previousVersion);
setTableDetails(res.data);
setColumns(columns);
setTableTags(getTableTags(columns || []));
@ -161,9 +158,8 @@ const DatasetDetailsPage: FunctionComponent = () => {
return new Promise<void>((resolve, reject) => {
saveUpdatedTableData(updatedTable)
.then((res) => {
const { version, changeDescription, owner, tags } = res.data;
const { version, owner, tags } = res.data;
setCurrentVersion(version);
setPreviousVersion(changeDescription.previousVersion);
setTableDetails(res.data);
setOwner(getOwnerFromId(owner?.id));
setTier(getTierTags(tags));
@ -191,7 +187,9 @@ const DatasetDetailsPage: FunctionComponent = () => {
};
const versionHandler = () => {
history.push(getDatasetVersionPath(tableFQN, currentVersion as string));
history.push(
getVersionPath(EntityType.TABLE, tableFQN, currentVersion as string)
);
};
const setLeafNode = (val: EntityLineage, pos: LineagePos) => {
@ -242,14 +240,12 @@ const DatasetDetailsPage: FunctionComponent = () => {
sampleData,
tableProfile,
version,
changeDescription,
service,
serviceType,
} = res.data;
setTableDetails(res.data);
setTableId(id);
setCurrentVersion(version);
setPreviousVersion(changeDescription?.previousVersion);
setTier(getTierTags(tags));
setOwner(getOwnerFromId(owner?.id));
setFollowers(followers);

View File

@ -14,30 +14,45 @@
import { AxiosError, AxiosResponse } from 'axios';
import React, { FunctionComponent, useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router-dom';
import { getDatabase } from '../../axiosAPIs/databaseAPI';
import { getServiceById } from '../../axiosAPIs/serviceAPI';
import {
getTableDetailsByFQN,
getTableVersion,
getTableVersions,
} from '../../axiosAPIs/tableAPI';
import {
getTopicByFqn,
getTopicVersion,
getTopicVersions,
} from '../../axiosAPIs/topicsAPI';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import DatasetVersion from '../../components/DatasetVersion/DatasetVersion.component';
import Loader from '../../components/Loader/Loader';
import TopicVersion from '../../components/TopicVersion/TopicVersion.component';
import {
getDatabaseDetailsPath,
getDatasetDetailsPath,
getDatasetVersionPath,
getServiceDetailsPath,
getTopicDetailsPath,
getVersionPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum';
import { ServiceCategory } from '../../enums/service.enum';
import { Dashboard } from '../../generated/entity/data/dashboard';
import { Pipeline } from '../../generated/entity/data/pipeline';
import { Table } from '../../generated/entity/data/table';
import { Topic } from '../../generated/entity/data/topic';
import { EntityHistory } from '../../generated/type/entityHistory';
import { TagLabel } from '../../generated/type/tagLabel';
import useToastContext from '../../hooks/useToastContext';
import { getPartialNameFromFQN } from '../../utils/CommonUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getOwnerFromId, getTierTags } from '../../utils/TableUtils';
export type VersionData = Partial<Table> &
Partial<Topic> &
Partial<Dashboard> &
Partial<Pipeline>;
const EntityVersionPage: FunctionComponent = () => {
const history = useHistory();
const showToast = useToastContext();
@ -45,183 +60,343 @@ const EntityVersionPage: FunctionComponent = () => {
const [owner, setOwner] = useState<
Table['owner'] & { displayName?: string }
>();
const [currentVersionData, setCurrentVersionData] = useState<Table>(
{} as Table
const [currentVersionData, setCurrentVersionData] = useState<VersionData>(
{} as VersionData
);
const { version, datasetFQN } = useParams() as Record<string, string>;
const { entityType, version, entityFQN } = useParams() as Record<
string,
string
>;
const [isLoading, setIsloading] = useState<boolean>(false);
const [versionList, setVersionList] = useState<EntityHistory>(
{} as EntityHistory
);
const [isVersionLoading, setIsVersionLoading] = useState<boolean>(false);
const [slashedTableName, setSlashedTableName] = useState<
const [slashedEntityName, setSlashedEntityName] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const backHandler = () => {
history.push(getDatasetDetailsPath(datasetFQN));
switch (entityType) {
case EntityType.TABLE:
history.push(getDatasetDetailsPath(entityFQN));
break;
case EntityType.TOPIC:
history.push(getTopicDetailsPath(entityFQN));
break;
default:
break;
}
};
const versionHandler = (v = version) => {
history.push(getDatasetVersionPath(datasetFQN, v as string));
history.push(getVersionPath(entityType, entityFQN, v as string));
};
const setEntityState = (
tags: TagLabel[],
owner: Table['owner'],
data: VersionData,
titleBreadCrumb: TitleBreadcrumbProps['titleLinks']
) => {
setTier(getTierTags(tags));
setOwner(getOwnerFromId(owner?.id));
setCurrentVersionData(data);
setSlashedEntityName(titleBreadCrumb);
};
const fetchEntityVersions = () => {
setIsloading(true);
getTableDetailsByFQN(
getPartialNameFromFQN(datasetFQN, ['service', 'database', 'table'], '.'),
['owner', 'tags']
)
.then((res: AxiosResponse) => {
const { id, owner, tags, name, database } = res.data;
setTier(getTierTags(tags));
setOwner(getOwnerFromId(owner?.id));
setCurrentVersionData(res.data);
getDatabase(database.id).then((resDB: AxiosResponse) => {
getServiceById('databaseServices', resDB.data.service?.id).then(
(resService: AxiosResponse) => {
setSlashedTableName([
{
name: resService.data.name,
url: resService.data.name
? getServiceDetailsPath(
resService.data.name,
resService.data.serviceType,
ServiceCategory.DATABASE_SERVICES
)
: '',
imgSrc: resService.data.serviceType
? serviceTypeLogo(resService.data.serviceType)
: undefined,
},
{
name: resDB.data.name,
url: getDatabaseDetailsPath(resDB.data.fullyQualifiedName),
},
{
name: name,
url: '',
activeTitle: true,
},
]);
}
);
});
getTableVersions(id)
.then((vres: AxiosResponse) => {
setVersionList(vres.data);
setIsloading(false);
switch (entityType) {
case EntityType.TABLE: {
getTableDetailsByFQN(
getPartialNameFromFQN(
entityFQN,
['service', 'database', 'table'],
'.'
),
['owner', 'tags']
)
.then((res: AxiosResponse) => {
const { id, owner, tags, name, database, service, serviceType } =
res.data;
setEntityState(tags, owner, res.data, [
{
name: service.name,
url: service.name
? getServiceDetailsPath(
service.name,
serviceType,
ServiceCategory.DATABASE_SERVICES
)
: '',
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
},
{
name: database.name,
url: getDatabaseDetailsPath(database.fullyQualifiedName),
},
{
name: name,
url: '',
activeTitle: true,
},
]);
getTableVersions(id)
.then((vres: AxiosResponse) => {
setVersionList(vres.data);
setIsloading(false);
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body: msg ?? `Error while fetching ${entityFQN} versions`,
});
});
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body: msg ?? `Error while fetching ${datasetFQN} versions`,
body: msg ?? `Error while fetching ${entityFQN} versions`,
});
});
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body: msg ?? `Error while fetching ${datasetFQN} versions`,
});
});
break;
}
case EntityType.TOPIC: {
getTopicByFqn(
getPartialNameFromFQN(entityFQN, ['service', 'database'], '.'),
['owner', 'tags']
)
.then((res: AxiosResponse) => {
const { id, owner, tags, name, service, serviceType } = res.data;
setEntityState(tags, owner, res.data, [
{
name: service.name,
url: service.name
? getServiceDetailsPath(
service.name,
serviceType,
ServiceCategory.MESSAGING_SERVICES
)
: '',
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
},
{
name: name,
url: '',
activeTitle: true,
},
]);
getTopicVersions(id)
.then((vres: AxiosResponse) => {
setVersionList(vres.data);
setIsloading(false);
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body: msg ?? `Error while fetching ${entityFQN} versions`,
});
});
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body: msg ?? `Error while fetching ${entityFQN} versions`,
});
});
break;
}
default:
break;
}
};
const fetchCurrentVersion = () => {
setIsVersionLoading(true);
getTableDetailsByFQN(
getPartialNameFromFQN(datasetFQN, ['service', 'database', 'table'], '.')
)
.then((res: AxiosResponse) => {
const { id, database, name } = res.data;
getDatabase(database.id).then((resDB: AxiosResponse) => {
getServiceById('databaseServices', resDB.data.service?.id).then(
(resService: AxiosResponse) => {
setSlashedTableName([
{
name: resService.data.name,
url: resService.data.name
? getServiceDetailsPath(
resService.data.name,
resService.data.serviceType,
ServiceCategory.DATABASE_SERVICES
)
: '',
imgSrc: resService.data.serviceType
? serviceTypeLogo(resService.data.serviceType)
: undefined,
},
{
name: resDB.data.name,
url: getDatabaseDetailsPath(resDB.data.fullyQualifiedName),
},
{
name: name,
url: '',
activeTitle: true,
},
]);
}
);
});
getTableVersion(id, version)
.then((vRes: AxiosResponse) => {
const { owner, tags } = vRes.data;
setTier(getTierTags(tags));
setOwner(getOwnerFromId(owner?.id));
setCurrentVersionData(vRes.data);
setIsVersionLoading(false);
switch (entityType) {
case EntityType.TABLE: {
getTableDetailsByFQN(
getPartialNameFromFQN(
entityFQN,
['service', 'database', 'table'],
'.'
)
)
.then((res: AxiosResponse) => {
const { id, database, name, service, serviceType } = res.data;
getTableVersion(id, version)
.then((vRes: AxiosResponse) => {
const { owner, tags } = vRes.data;
setEntityState(tags, owner, vRes.data, [
{
name: service.name,
url: service.name
? getServiceDetailsPath(
service.name,
serviceType,
ServiceCategory.DATABASE_SERVICES
)
: '',
imgSrc: serviceType
? serviceTypeLogo(serviceType)
: undefined,
},
{
name: database.name,
url: getDatabaseDetailsPath(database.fullyQualifiedName),
},
{
name: name,
url: '',
activeTitle: true,
},
]);
setIsVersionLoading(false);
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body:
msg ??
`Error while fetching ${entityFQN} version ${version}`,
});
});
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body:
msg ?? `Error while fetching ${datasetFQN} version ${version}`,
msg ?? `Error while fetching ${entityFQN} version ${version}`,
});
});
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body: msg ?? `Error while fetching ${datasetFQN} version ${version}`,
});
});
break;
}
case EntityType.TOPIC: {
getTopicByFqn(
getPartialNameFromFQN(entityFQN, ['service', 'database'], '.')
)
.then((res: AxiosResponse) => {
const { id, name, service, serviceType } = res.data;
getTopicVersion(id, version)
.then((vRes: AxiosResponse) => {
const { owner, tags } = vRes.data;
setEntityState(tags, owner, vRes.data, [
{
name: service.name,
url: service.name
? getServiceDetailsPath(
service.name,
serviceType,
ServiceCategory.MESSAGING_SERVICES
)
: '',
imgSrc: serviceType
? serviceTypeLogo(serviceType)
: undefined,
},
{
name: name,
url: '',
activeTitle: true,
},
]);
setIsVersionLoading(false);
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body:
msg ??
`Error while fetching ${entityFQN} version ${version}`,
});
});
})
.catch((err: AxiosError) => {
const msg = err.message;
showToast({
variant: 'error',
body:
msg ?? `Error while fetching ${entityFQN} version ${version}`,
});
});
break;
}
default:
break;
}
};
const versionComponent = () => {
switch (entityType) {
case EntityType.TABLE: {
return (
<DatasetVersion
backHandler={backHandler}
currentVersionData={currentVersionData}
datasetFQN={entityFQN}
isVersionLoading={isVersionLoading}
owner={owner}
slashedTableName={slashedEntityName}
tier={tier as TagLabel}
version={version}
versionHandler={versionHandler}
versionList={versionList}
/>
);
}
case EntityType.TOPIC: {
return (
<TopicVersion
backHandler={backHandler}
currentVersionData={currentVersionData}
isVersionLoading={isVersionLoading}
owner={owner}
slashedTopicName={slashedEntityName}
tier={tier as TagLabel}
topicFQN={entityFQN}
version={version}
versionHandler={versionHandler}
versionList={versionList}
/>
);
}
default:
return null;
}
};
useEffect(() => {
fetchEntityVersions();
}, [datasetFQN]);
}, [entityFQN]);
useEffect(() => {
fetchCurrentVersion();
}, [version]);
return (
<>
{isLoading ? (
<Loader />
) : (
<DatasetVersion
backHandler={backHandler}
currentVersionData={currentVersionData}
datasetFQN={datasetFQN}
isVersionLoading={isVersionLoading}
owner={owner}
slashedTableName={slashedTableName}
tier={tier as TagLabel}
version={version}
versionHandler={versionHandler}
versionList={versionList}
/>
)}
</>
);
return <>{isLoading ? <Loader /> : versionComponent()}</>;
};
export default EntityVersionPage;

View File

@ -30,6 +30,7 @@ import TopicDetails from '../../components/TopicDetails/TopicDetails.component';
import {
getServiceDetailsPath,
getTopicDetailsPath,
getVersionPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum';
import { ServiceCategory } from '../../enums/service.enum';
@ -78,6 +79,7 @@ const TopicDetailsPage: FunctionComponent = () => {
const [slashedTopicName, setSlashedTopicName] = useState<
TitleBreadcrumbProps['titleLinks']
>([]);
const [currentVersion, setCurrentVersion] = useState<string>();
const activeTabHandler = (tabValue: number) => {
const currentTabIndex = tabValue - 1;
@ -134,10 +136,12 @@ const TopicDetailsPage: FunctionComponent = () => {
replicationFactor,
retentionSize,
serviceType,
version,
} = res.data;
setName(name);
setTopicDetails(res.data);
setTopicId(id);
setCurrentVersion(version);
setDescription(description ?? '');
setSchemaType(schemaType);
setFollowers(followers);
@ -206,7 +210,8 @@ const TopicDetailsPage: FunctionComponent = () => {
const descriptionUpdateHandler = (updatedTopic: Topic) => {
saveUpdatedTopicData(updatedTopic).then((res: AxiosResponse) => {
const { description } = res.data;
const { description, version } = res.data;
setCurrentVersion(version);
setTopicDetails(res.data);
setDescription(description);
});
@ -217,6 +222,7 @@ const TopicDetailsPage: FunctionComponent = () => {
saveUpdatedTopicData(updatedTopic)
.then((res) => {
setTopicDetails(res.data);
setCurrentVersion(res.data.version);
setOwner(getOwnerFromId(res.data.owner?.id));
setTier(getTierTags(res.data.tags));
resolve();
@ -228,10 +234,17 @@ const TopicDetailsPage: FunctionComponent = () => {
const onTagUpdate = (updatedTopic: Topic) => {
saveUpdatedTopicData(updatedTopic).then((res: AxiosResponse) => {
setTier(getTierTags(res.data.tags));
setCurrentVersion(res.data.version);
setTags(getTagsWithoutTier(res.data.tags));
});
};
const versionHandler = () => {
history.push(
getVersionPath(EntityType.TOPIC, topicFQN, currentVersion as string)
);
};
useEffect(() => {
fetchTopicDetail(topicFQN);
}, [topicFQN]);
@ -270,6 +283,8 @@ const TopicDetailsPage: FunctionComponent = () => {
topicTags={tags}
unfollowTopicHandler={unfollowTopic}
users={AppState.users}
version={currentVersion}
versionHandler={versionHandler}
/>
)}
</>

View File

@ -67,6 +67,7 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
/>
<Route exact component={TopicDetailsPage} path={ROUTES.TOPIC_DETAILS} />
<Route
exact
component={TopicDetailsPage}
path={ROUTES.TOPIC_DETAILS_WITH_TAB}
/>
@ -91,11 +92,7 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
path={ROUTES.PIPELINE_DETAILS_WITH_TAB}
/>
<Route component={Onboarding} path={ROUTES.ONBOARDING} />
<Route
exact
component={EntityVersionPage}
path={ROUTES.DATASET_VERSION}
/>
<Route exact component={EntityVersionPage} path={ROUTES.ENTITY_VERSION} />
<Route exact component={IngestionPage} path={ROUTES.INGESTION} />
{isAuthDisabled || isAdminUser ? (
<Route exact component={UserListPage} path={ROUTES.USER_LIST} />