Feat: add parma link support for entity tabs (#1128)

* Feat: add parma link support for dataset tabs

* Feat: add parma link support for dashboard, pipeline and topic page

* Fix: miner fix for explorepage

* code re-factore and add requested changes

* Feat: Add deep link support for schema and sample data tab in dataset details

* change url format for schema and sample table tab
This commit is contained in:
Shailesh Parmar 2021-11-11 21:50:43 +05:30 committed by GitHub
parent fa3c9b1758
commit 365b62d9d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 443 additions and 110 deletions

View File

@ -16,7 +16,7 @@
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { cloneDeep, isUndefined } from 'lodash'; import { cloneDeep } from 'lodash';
import { import {
AggregationType, AggregationType,
FilterObject, FilterObject,
@ -456,7 +456,7 @@ const Explore: React.FC<ExploreProps> = ({
useEffect(() => { useEffect(() => {
if (!isMounting.current && previsouIndex === getCurrentIndex(tab)) { if (!isMounting.current && previsouIndex === getCurrentIndex(tab)) {
forceSetAgg.current = isUndefined(tab); forceSetAgg.current = false;
fetchTableData(); fetchTableData();
} }
}, [currentPage, filters, sortField, sortOrder]); }, [currentPage, filters, sortField, sortOrder]);

View File

@ -15,8 +15,11 @@
* limitations under the License. * limitations under the License.
*/ */
import { lowerCase } from 'lodash'; import { isUndefined, lowerCase } from 'lodash';
import React, { FunctionComponent, useState } from 'react'; import { DatasetSchemaTableTab } from 'Models';
import React, { FunctionComponent, useEffect, useState } from 'react';
import { useHistory, useParams } from 'react-router';
import { getDatasetTabPath } from '../../constants/constants';
import { import {
ColumnJoins, ColumnJoins,
Table, Table,
@ -49,17 +52,32 @@ const SchemaTab: FunctionComponent<Props> = ({
owner, owner,
isReadOnly = false, isReadOnly = false,
}: Props) => { }: Props) => {
const history = useHistory();
const { datasetFQN: tableFQN, tab } = useParams() as Record<string, string>;
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
const [checkedValue, setCheckedValue] = useState('schema'); const [checkedValue, setCheckedValue] =
useState<DatasetSchemaTableTab>('schema');
const handleSearchAction = (searchValue: string) => { const handleSearchAction = (searchValue: string) => {
setSearchText(searchValue); setSearchText(searchValue);
}; };
const handleToggleChange = (value: string) => { const handleToggleChange = (value: DatasetSchemaTableTab) => {
setCheckedValue(value); setCheckedValue(value);
history.push({
pathname: getDatasetTabPath(tableFQN, value),
});
}; };
useEffect(() => {
if (tab && ['schema', 'sample_data'].includes(tab)) {
const activeTab = isUndefined(tab)
? 'schema'
: (tab as DatasetSchemaTableTab);
setCheckedValue(activeTab);
}
}, [tab]);
const getToggleButtonClasses = (type: string): string => { const getToggleButtonClasses = (type: string): string => {
return ( return (
'tw-flex-1 tw-text-primary tw-font-medium tw-border tw-border-transparent tw-rounded tw-py-1 tw-px-2 focus:tw-outline-none' + 'tw-flex-1 tw-text-primary tw-font-medium tw-border tw-border-transparent tw-rounded tw-py-1 tw-px-2 focus:tw-outline-none' +
@ -115,10 +133,10 @@ const SchemaTab: FunctionComponent<Props> = ({
Schema Schema
</button> </button>
<button <button
className={getToggleButtonClasses('sample-data')} className={getToggleButtonClasses('sample_data')}
data-testid="sample-data-button" data-testid="sample-data-button"
onClick={() => { onClick={() => {
handleToggleChange('sample-data'); handleToggleChange('sample_data');
}}> }}>
Sample Data Sample Data
</button> </button>

View File

@ -123,11 +123,15 @@ export const ROUTES = {
SIGNUP: '/signup', SIGNUP: '/signup',
SIGNIN: '/signin', SIGNIN: '/signin',
DATASET_DETAILS: `/dataset/${PLACEHOLDER_ROUTE_DATASET_FQN}`, 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}`, DATASET_VERSION: `/dataset/${PLACEHOLDER_ROUTE_DATASET_FQN}/versions/${PLAEHOLDER_ROUTE_VERSION}`,
TOPIC_DETAILS: `/topic/${PLACEHOLDER_ROUTE_TOPIC_FQN}`, 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}`, DASHBOARD_DETAILS: `/dashboard/${PLACEHOLDER_ROUTE_DASHBOARD_FQN}`,
DASHBOARD_DETAILS_WITH_TAB: `/dashboard/${PLACEHOLDER_ROUTE_DASHBOARD_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
DATABASE_DETAILS: `/database/${PLACEHOLDER_ROUTE_DATABASE_FQN}`, DATABASE_DETAILS: `/database/${PLACEHOLDER_ROUTE_DATABASE_FQN}`,
PIPELINE_DETAILS: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}`, PIPELINE_DETAILS: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}`,
PIPELINE_DETAILS_WITH_TAB: `/pipeline/${PLACEHOLDER_ROUTE_PIPELINE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
ONBOARDING: '/onboarding', ONBOARDING: '/onboarding',
}; };
@ -144,6 +148,7 @@ export const getDatasetDetailsPath = (
return `${path}${columnName ? `.${columnName}` : ''}`; return `${path}${columnName ? `.${columnName}` : ''}`;
}; };
export const getDatasetVersionPath = (datasetFQN: string, version: string) => { export const getDatasetVersionPath = (datasetFQN: string, version: string) => {
let path = ROUTES.DATASET_VERSION; let path = ROUTES.DATASET_VERSION;
path = path path = path
@ -153,6 +158,15 @@ export const getDatasetVersionPath = (datasetFQN: string, version: string) => {
return path; return path;
}; };
export const getDatasetTabPath = (datasetFQN: string, tab = 'schema') => {
let path = ROUTES.DATASET_DETAILS_WITH_TAB;
path = path
.replace(PLACEHOLDER_ROUTE_DATASET_FQN, datasetFQN)
.replace(PLACEHOLDER_ROUTE_TAB, tab);
return path;
};
export const getServiceDetailsPath = ( export const getServiceDetailsPath = (
serviceFQN: string, serviceFQN: string,
serviceType: string serviceType: string
@ -181,24 +195,39 @@ export const getDatabaseDetailsPath = (databaseFQN: string) => {
return path; return path;
}; };
export const getTopicDetailsPath = (topicFQN: string) => { export const getTopicDetailsPath = (topicFQN: string, tab?: string) => {
let path = ROUTES.TOPIC_DETAILS; let path = tab ? ROUTES.TOPIC_DETAILS_WITH_TAB : ROUTES.TOPIC_DETAILS;
path = path.replace(PLACEHOLDER_ROUTE_TOPIC_FQN, topicFQN); path = path.replace(PLACEHOLDER_ROUTE_TOPIC_FQN, topicFQN);
if (tab) {
path = path.replace(PLACEHOLDER_ROUTE_TAB, tab);
}
return path; return path;
}; };
export const getDashboardDetailsPath = (dashboardFQN: string) => {
let path = ROUTES.DASHBOARD_DETAILS; export const getDashboardDetailsPath = (dashboardFQN: string, tab?: string) => {
let path = tab ? ROUTES.DASHBOARD_DETAILS_WITH_TAB : ROUTES.DASHBOARD_DETAILS;
path = path.replace(PLACEHOLDER_ROUTE_DASHBOARD_FQN, dashboardFQN); path = path.replace(PLACEHOLDER_ROUTE_DASHBOARD_FQN, dashboardFQN);
return path; if (tab) {
}; path = path.replace(PLACEHOLDER_ROUTE_TAB, tab);
export const getPipelineDetailsPath = (pipelineFQN: string) => { }
let path = ROUTES.PIPELINE_DETAILS;
path = path.replace(PLACEHOLDER_ROUTE_PIPELINE_FQN, pipelineFQN);
return path; return path;
}; };
export const getPipelineDetailsPath = (pipelineFQN: string, tab?: string) => {
let path = tab ? ROUTES.PIPELINE_DETAILS_WITH_TAB : ROUTES.PIPELINE_DETAILS;
path = path.replace(PLACEHOLDER_ROUTE_PIPELINE_FQN, pipelineFQN);
if (tab) {
path = path.replace(PLACEHOLDER_ROUTE_TAB, tab);
}
return path;
};
export const getTeamDetailsPath = (teamName: string) => { export const getTeamDetailsPath = (teamName: string) => {
let path = ROUTES.TEAM_DETAILS; let path = ROUTES.TEAM_DETAILS;
path = path.replace(PLACEHOLDER_ROUTE_TEAM, teamName); path = path.replace(PLACEHOLDER_ROUTE_TEAM, teamName);

View File

@ -393,4 +393,6 @@ declare module 'Models' {
export interface RecentlyViewed { export interface RecentlyViewed {
data: Array<RecentlyViewedData>; data: Array<RecentlyViewedData>;
} }
export type DatasetSchemaTableTab = 'schema' | 'sample_data';
} }

View File

@ -2,7 +2,7 @@ import { AxiosPromise, AxiosResponse } from 'axios';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
import { EntityTags, TableDetail } from 'Models'; import { EntityTags, TableDetail } from 'Models';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import AppState from '../../AppState'; import AppState from '../../AppState';
import { getChartById, updateChart } from '../../axiosAPIs/chartAPI'; import { getChartById, updateChart } from '../../axiosAPIs/chartAPI';
import { import {
@ -15,12 +15,19 @@ import { getServiceById } from '../../axiosAPIs/serviceAPI';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface'; import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import DashboardDetails from '../../components/DashboardDetails/DashboardDetails.component'; import DashboardDetails from '../../components/DashboardDetails/DashboardDetails.component';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
import { getServiceDetailsPath } from '../../constants/constants'; import {
getDashboardDetailsPath,
getServiceDetailsPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { Chart } from '../../generated/entity/data/chart'; import { Chart } from '../../generated/entity/data/chart';
import { Dashboard } from '../../generated/entity/data/dashboard'; import { Dashboard } from '../../generated/entity/data/dashboard';
import { User } from '../../generated/entity/teams/user'; import { User } from '../../generated/entity/teams/user';
import { addToRecentViewed, getCurrentUserId } from '../../utils/CommonUtils'; import { addToRecentViewed, getCurrentUserId } from '../../utils/CommonUtils';
import {
dashboardDetailsTabs,
getCurrentDashboardTab,
} from '../../utils/DashboardDetailsUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { import {
getOwnerFromId, getOwnerFromId,
@ -31,10 +38,13 @@ import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
type ChartType = { type ChartType = {
displayName: string; displayName: string;
} & Chart; } & Chart;
const DashboardDetailsPage = () => { const DashboardDetailsPage = () => {
const USERId = getCurrentUserId(); const USERId = getCurrentUserId();
const history = useHistory();
const [tagList, setTagList] = useState<Array<string>>([]); const [tagList, setTagList] = useState<Array<string>>([]);
const { dashboardFQN } = useParams() as Record<string, string>; const { dashboardFQN, tab } = useParams() as Record<string, string>;
const [dashboardDetails, setDashboardDetails] = useState<Dashboard>( const [dashboardDetails, setDashboardDetails] = useState<Dashboard>(
{} as Dashboard {} as Dashboard
); );
@ -45,7 +55,9 @@ const DashboardDetailsPage = () => {
const [owner, setOwner] = useState<TableDetail['owner']>(); const [owner, setOwner] = useState<TableDetail['owner']>();
const [tier, setTier] = useState<string>(); const [tier, setTier] = useState<string>();
const [tags, setTags] = useState<Array<EntityTags>>([]); const [tags, setTags] = useState<Array<EntityTags>>([]);
const [activeTab, setActiveTab] = useState<number>(1); const [activeTab, setActiveTab] = useState<number>(
getCurrentDashboardTab(tab)
);
const [charts, setCharts] = useState<ChartType[]>([]); const [charts, setCharts] = useState<ChartType[]>([]);
const [dashboardUrl, setDashboardUrl] = useState<string>(''); const [dashboardUrl, setDashboardUrl] = useState<string>('');
const [displayName, setDisplayName] = useState<string>(''); const [displayName, setDisplayName] = useState<string>('');
@ -55,9 +67,26 @@ const DashboardDetailsPage = () => {
>([]); >([]);
const activeTabHandler = (tabValue: number) => { const activeTabHandler = (tabValue: number) => {
setActiveTab(tabValue); const currentTabIndex = tabValue - 1;
if (dashboardDetailsTabs[currentTabIndex].path !== tab) {
setActiveTab(
getCurrentDashboardTab(dashboardDetailsTabs[currentTabIndex].path)
);
history.push({
pathname: getDashboardDetailsPath(
dashboardFQN,
dashboardDetailsTabs[currentTabIndex].path
),
});
}
}; };
useEffect(() => {
if (dashboardDetailsTabs[activeTab - 1].path !== tab) {
setActiveTab(getCurrentDashboardTab(tab));
}
}, [tab]);
const saveUpdatedDashboardData = ( const saveUpdatedDashboardData = (
updatedData: Dashboard updatedData: Dashboard
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {

View File

@ -36,6 +36,7 @@ import DatasetDetails from '../../components/DatasetDetails/DatasetDetails.compo
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
import { import {
getDatabaseDetailsPath, getDatabaseDetailsPath,
getDatasetTabPath,
getDatasetVersionPath, getDatasetVersionPath,
getServiceDetailsPath, getServiceDetailsPath,
} from '../../constants/constants'; } from '../../constants/constants';
@ -53,13 +54,18 @@ import {
getCurrentUserId, getCurrentUserId,
getPartialNameFromFQN, getPartialNameFromFQN,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
import {
datasetTableTabs,
getCurrentDatasetTab,
} from '../../utils/DatasetDetailsUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { getOwnerFromId, getTierFromTableTags } from '../../utils/TableUtils'; import { getOwnerFromId, getTierFromTableTags } from '../../utils/TableUtils';
import { getTableTags } from '../../utils/TagsUtils'; import { getTableTags } from '../../utils/TagsUtils';
const DatasetDetailsPage: FunctionComponent = () => { const DatasetDetailsPage: FunctionComponent = () => {
const history = useHistory(); const history = useHistory();
const [isLoading, setIsLoading] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(true);
const [isLineageLoading, setIsLineageLoading] = useState<boolean>(true);
const USERId = getCurrentUserId(); const USERId = getCurrentUserId();
const [tableId, setTableId] = useState(''); const [tableId, setTableId] = useState('');
const [tier, setTier] = useState<string>(); const [tier, setTier] = useState<string>();
@ -85,8 +91,8 @@ const DatasetDetailsPage: FunctionComponent = () => {
}); });
const [tableProfile, setTableProfile] = useState<Table['tableProfile']>([]); const [tableProfile, setTableProfile] = useState<Table['tableProfile']>([]);
const [tableDetails, setTableDetails] = useState<Table>({} as Table); const [tableDetails, setTableDetails] = useState<Table>({} as Table);
const [activeTab, setActiveTab] = useState<number>(1); const { datasetFQN: tableFQN, tab } = useParams() as Record<string, string>;
const { datasetFQN: tableFQN } = useParams() as Record<string, string>; const [activeTab, setActiveTab] = useState<number>(getCurrentDatasetTab(tab));
const [entityLineage, setEntityLineage] = useState<EntityLineage>( const [entityLineage, setEntityLineage] = useState<EntityLineage>(
{} as EntityLineage {} as EntityLineage
); );
@ -98,9 +104,26 @@ const DatasetDetailsPage: FunctionComponent = () => {
const [, setPreviousVersion] = useState<string>(); const [, setPreviousVersion] = useState<string>();
const activeTabHandler = (tabValue: number) => { const activeTabHandler = (tabValue: number) => {
setActiveTab(tabValue); const currentTabIndex = tabValue - 1;
if (datasetTableTabs[currentTabIndex].path !== tab) {
setActiveTab(
getCurrentDatasetTab(datasetTableTabs[currentTabIndex].path)
);
history.push({
pathname: getDatasetTabPath(
tableFQN,
datasetTableTabs[currentTabIndex].path
),
});
}
}; };
useEffect(() => {
if (datasetTableTabs[activeTab - 1].path !== tab) {
setActiveTab(getCurrentDatasetTab(tab));
}
}, [tab]);
const saveUpdatedTableData = (updatedData: Table): Promise<AxiosResponse> => { const saveUpdatedTableData = (updatedData: Table): Promise<AxiosResponse> => {
const jsonPatch = compare(tableDetails, updatedData); const jsonPatch = compare(tableDetails, updatedData);
@ -241,21 +264,24 @@ const DatasetDetailsPage: FunctionComponent = () => {
setTableTags(getTableTags(columns || [])); setTableTags(getTableTags(columns || []));
setUsageSummary(usageSummary); setUsageSummary(usageSummary);
setJoins(joins); setJoins(joins);
setIsLoading(false);
}) })
.catch(() => { .finally(() => {
setIsLoading(false); setIsLoading(false);
}); });
setActiveTab(1); getLineageByFQN(tableFQN, EntityType.TABLE)
getLineageByFQN(tableFQN, EntityType.TABLE).then((res: AxiosResponse) => .then((res: AxiosResponse) => {
setEntityLineage(res.data) setEntityLineage(res.data);
); })
.finally(() => {
setIsLineageLoading(false);
});
setActiveTab(getCurrentDatasetTab(tab));
}, [tableFQN]); }, [tableFQN]);
return ( return (
<> <>
{isLoading ? ( {isLoading || isLineageLoading ? (
<Loader /> <Loader />
) : ( ) : (
<DatasetDetails <DatasetDetails

View File

@ -3,7 +3,7 @@ import { compare, Operation } from 'fast-json-patch';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { EntityTags, TableDetail } from 'Models'; import { EntityTags, TableDetail } from 'Models';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import AppState from '../../AppState'; import AppState from '../../AppState';
import { getLineageByFQN } from '../../axiosAPIs/lineageAPI'; import { getLineageByFQN } from '../../axiosAPIs/lineageAPI';
import { import {
@ -16,12 +16,19 @@ import { getServiceById } from '../../axiosAPIs/serviceAPI';
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface'; import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
import PipelineDetails from '../../components/PipelineDetails/PipelineDetails.component'; import PipelineDetails from '../../components/PipelineDetails/PipelineDetails.component';
import { getServiceDetailsPath } from '../../constants/constants'; import {
getPipelineDetailsPath,
getServiceDetailsPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { Pipeline, Task } from '../../generated/entity/data/pipeline'; import { Pipeline, Task } from '../../generated/entity/data/pipeline';
import { User } from '../../generated/entity/teams/user'; import { User } from '../../generated/entity/teams/user';
import { EntityLineage } from '../../generated/type/entityLineage'; import { EntityLineage } from '../../generated/type/entityLineage';
import { addToRecentViewed, getCurrentUserId } from '../../utils/CommonUtils'; import { addToRecentViewed, getCurrentUserId } from '../../utils/CommonUtils';
import {
getCurrentPipelineTab,
pipelineDetailsTabs,
} from '../../utils/PipelineDetailsUtils';
import { serviceTypeLogo } from '../../utils/ServiceUtils'; import { serviceTypeLogo } from '../../utils/ServiceUtils';
import { import {
getOwnerFromId, getOwnerFromId,
@ -32,20 +39,24 @@ import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
const PipelineDetailsPage = () => { const PipelineDetailsPage = () => {
const USERId = getCurrentUserId(); const USERId = getCurrentUserId();
const history = useHistory();
const [tagList, setTagList] = useState<Array<string>>([]); const [tagList, setTagList] = useState<Array<string>>([]);
const { pipelineFQN } = useParams() as Record<string, string>; const { pipelineFQN, tab } = useParams() as Record<string, string>;
const [pipelineDetails, setPipelineDetails] = useState<Pipeline>( const [pipelineDetails, setPipelineDetails] = useState<Pipeline>(
{} as Pipeline {} as Pipeline
); );
const [pipelineId, setPipelineId] = useState<string>(''); const [pipelineId, setPipelineId] = useState<string>('');
const [isLoading, setLoading] = useState<boolean>(false); const [isLoading, setLoading] = useState<boolean>(true);
const [isLineageLoading, setIsLineageLoading] = useState<boolean>(true);
const [description, setDescription] = useState<string>(''); const [description, setDescription] = useState<string>('');
const [followers, setFollowers] = useState<Array<User>>([]); const [followers, setFollowers] = useState<Array<User>>([]);
const [owner, setOwner] = useState<TableDetail['owner']>(); const [owner, setOwner] = useState<TableDetail['owner']>();
const [tier, setTier] = useState<string>(); const [tier, setTier] = useState<string>();
const [tags, setTags] = useState<Array<EntityTags>>([]); const [tags, setTags] = useState<Array<EntityTags>>([]);
const [activeTab, setActiveTab] = useState<number>(1); const [activeTab, setActiveTab] = useState<number>(
getCurrentPipelineTab(tab)
);
const [tasks, setTasks] = useState<Task[]>([]); const [tasks, setTasks] = useState<Task[]>([]);
const [pipelineUrl, setPipelineUrl] = useState<string>(''); const [pipelineUrl, setPipelineUrl] = useState<string>('');
const [displayName, setDisplayName] = useState<string>(''); const [displayName, setDisplayName] = useState<string>('');
@ -58,9 +69,26 @@ const PipelineDetailsPage = () => {
{} as EntityLineage {} as EntityLineage
); );
const activeTabHandler = (tabValue: number) => { const activeTabHandler = (tabValue: number) => {
setActiveTab(tabValue); const currentTabIndex = tabValue - 1;
if (pipelineDetailsTabs[currentTabIndex].path !== tab) {
setActiveTab(
getCurrentPipelineTab(pipelineDetailsTabs[currentTabIndex].path)
);
history.push({
pathname: getPipelineDetailsPath(
pipelineFQN,
pipelineDetailsTabs[currentTabIndex].path
),
});
}
}; };
useEffect(() => {
if (pipelineDetailsTabs[activeTab - 1].path !== tab) {
setActiveTab(getCurrentPipelineTab(tab));
}
}, [tab]);
const saveUpdatedPipelineData = ( const saveUpdatedPipelineData = (
updatedData: Pipeline updatedData: Pipeline
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
@ -88,62 +116,63 @@ const PipelineDetailsPage = () => {
'followers', 'followers',
'tags', 'tags',
'tasks', 'tasks',
]).then((res: AxiosResponse) => { ])
const { .then((res: AxiosResponse) => {
id, const {
description, id,
followers, description,
fullyQualifiedName, followers,
service, fullyQualifiedName,
tags, service,
owner, tags,
displayName, owner,
tasks, displayName,
pipelineUrl, tasks,
} = res.data; pipelineUrl,
setDisplayName(displayName); } = res.data;
setPipelineDetails(res.data); setDisplayName(displayName);
setPipelineId(id); setPipelineDetails(res.data);
setDescription(description ?? ''); setPipelineId(id);
setFollowers(followers); setDescription(description ?? '');
setOwner(getOwnerFromId(owner?.id)); setFollowers(followers);
setTier(getTierFromTableTags(tags)); setOwner(getOwnerFromId(owner?.id));
setTags(getTagsWithoutTier(tags)); setTier(getTierFromTableTags(tags));
getServiceById('pipelineServices', service?.id).then( setTags(getTagsWithoutTier(tags));
(serviceRes: AxiosResponse) => { getServiceById('pipelineServices', service?.id).then(
setServiceType(serviceRes.data.serviceType); (serviceRes: AxiosResponse) => {
setSlashedPipelineName([ setServiceType(serviceRes.data.serviceType);
{ setSlashedPipelineName([
name: serviceRes.data.name, {
url: serviceRes.data.name name: serviceRes.data.name,
? getServiceDetailsPath( url: serviceRes.data.name
serviceRes.data.name, ? getServiceDetailsPath(
serviceRes.data.serviceType serviceRes.data.name,
) serviceRes.data.serviceType
: '', )
imgSrc: serviceRes.data.serviceType : '',
? serviceTypeLogo(serviceRes.data.serviceType) imgSrc: serviceRes.data.serviceType
: undefined, ? serviceTypeLogo(serviceRes.data.serviceType)
}, : undefined,
{ },
name: displayName, {
url: '', name: displayName,
activeTitle: true, url: '',
}, activeTitle: true,
]); },
]);
addToRecentViewed({ addToRecentViewed({
entityType: EntityType.PIPELINE, entityType: EntityType.PIPELINE,
fqn: fullyQualifiedName, fqn: fullyQualifiedName,
serviceType: serviceRes.data.serviceType, serviceType: serviceRes.data.serviceType,
timestamp: 0, timestamp: 0,
}); });
} }
); );
setPipelineUrl(pipelineUrl); setPipelineUrl(pipelineUrl);
setTasks(tasks); setTasks(tasks);
setLoading(false); })
}); .finally(() => setLoading(false));
}; };
const followPipeline = () => { const followPipeline = () => {
@ -196,10 +225,14 @@ const PipelineDetailsPage = () => {
useEffect(() => { useEffect(() => {
fetchPipelineDetail(pipelineFQN); fetchPipelineDetail(pipelineFQN);
setActiveTab(1); setActiveTab(getCurrentPipelineTab(tab));
getLineageByFQN(pipelineFQN, EntityType.PIPELINE).then( getLineageByFQN(pipelineFQN, EntityType.PIPELINE)
(res: AxiosResponse) => setEntityLineage(res.data) .then((res: AxiosResponse) => {
); setEntityLineage(res.data);
})
.finally(() => {
setIsLineageLoading(false);
});
}, [pipelineFQN]); }, [pipelineFQN]);
useEffect(() => { useEffect(() => {
@ -208,7 +241,7 @@ const PipelineDetailsPage = () => {
return ( return (
<> <>
{isLoading ? ( {isLoading || isLineageLoading ? (
<Loader /> <Loader />
) : ( ) : (
<PipelineDetails <PipelineDetails

View File

@ -3,7 +3,7 @@ import { compare } from 'fast-json-patch';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { EntityTags, TableDetail } from 'Models'; import { EntityTags, TableDetail } from 'Models';
import React, { FunctionComponent, useEffect, useState } from 'react'; import React, { FunctionComponent, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom';
import AppState from '../../AppState'; import AppState from '../../AppState';
import { getServiceById } from '../../axiosAPIs/serviceAPI'; import { getServiceById } from '../../axiosAPIs/serviceAPI';
import { import {
@ -15,7 +15,10 @@ import {
import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface'; import { TitleBreadcrumbProps } from '../../components/common/title-breadcrumb/title-breadcrumb.interface';
import Loader from '../../components/Loader/Loader'; import Loader from '../../components/Loader/Loader';
import TopicDetails from '../../components/TopicDetails/TopicDetails.component'; import TopicDetails from '../../components/TopicDetails/TopicDetails.component';
import { getServiceDetailsPath } from '../../constants/constants'; import {
getServiceDetailsPath,
getTopicDetailsPath,
} from '../../constants/constants';
import { EntityType } from '../../enums/entity.enum'; import { EntityType } from '../../enums/entity.enum';
import { Topic } from '../../generated/entity/data/topic'; import { Topic } from '../../generated/entity/data/topic';
import { User } from '../../generated/entity/teams/user'; import { User } from '../../generated/entity/teams/user';
@ -28,23 +31,28 @@ import {
getTierFromTableTags, getTierFromTableTags,
} from '../../utils/TableUtils'; } from '../../utils/TableUtils';
import { getTagCategories, getTaglist } from '../../utils/TagsUtils'; import { getTagCategories, getTaglist } from '../../utils/TagsUtils';
import {
getCurrentTopicTab,
topicDetailsTabs,
} from '../../utils/TopicDetailsUtils';
const TopicDetailsPage: FunctionComponent = () => { const TopicDetailsPage: FunctionComponent = () => {
const USERId = getCurrentUserId(); const USERId = getCurrentUserId();
const showToast = useToastContext(); const showToast = useToastContext();
const history = useHistory();
const [tagList, setTagList] = useState<Array<string>>([]); const [tagList, setTagList] = useState<Array<string>>([]);
const { topicFQN } = useParams() as Record<string, string>; const { topicFQN, tab } = useParams() as Record<string, string>;
const [topicDetails, setTopicDetails] = useState<Topic>({} as Topic); const [topicDetails, setTopicDetails] = useState<Topic>({} as Topic);
const [topicId, setTopicId] = useState<string>(''); const [topicId, setTopicId] = useState<string>('');
const [isLoading, setLoading] = useState<boolean>(false); const [isLoading, setLoading] = useState<boolean>(true);
const [description, setDescription] = useState<string>(''); const [description, setDescription] = useState<string>('');
const [followers, setFollowers] = useState<Array<User>>([]); const [followers, setFollowers] = useState<Array<User>>([]);
const [owner, setOwner] = useState<TableDetail['owner']>(); const [owner, setOwner] = useState<TableDetail['owner']>();
const [tier, setTier] = useState<string>(); const [tier, setTier] = useState<string>();
const [schemaType, setSchemaType] = useState<string>(''); const [schemaType, setSchemaType] = useState<string>('');
const [tags, setTags] = useState<Array<EntityTags>>([]); const [tags, setTags] = useState<Array<EntityTags>>([]);
const [activeTab, setActiveTab] = useState<number>(1); const [activeTab, setActiveTab] = useState<number>(getCurrentTopicTab(tab));
const [partitions, setPartitions] = useState<number>(0); const [partitions, setPartitions] = useState<number>(0);
const [cleanupPolicies, setCleanupPolicies] = useState<Array<string>>([]); const [cleanupPolicies, setCleanupPolicies] = useState<Array<string>>([]);
const [maximumMessageSize, setMaximumMessageSize] = useState<number>(0); const [maximumMessageSize, setMaximumMessageSize] = useState<number>(0);
@ -58,9 +66,24 @@ const TopicDetailsPage: FunctionComponent = () => {
>([]); >([]);
const activeTabHandler = (tabValue: number) => { const activeTabHandler = (tabValue: number) => {
setActiveTab(tabValue); const currentTabIndex = tabValue - 1;
if (topicDetailsTabs[currentTabIndex].path !== tab) {
setActiveTab(getCurrentTopicTab(topicDetailsTabs[currentTabIndex].path));
history.push({
pathname: getTopicDetailsPath(
topicFQN,
topicDetailsTabs[currentTabIndex].path
),
});
}
}; };
useEffect(() => {
if (topicDetailsTabs[activeTab - 1].path !== tab) {
setActiveTab(getCurrentTopicTab(tab));
}
}, [tab]);
const saveUpdatedTopicData = (updatedData: Topic): Promise<AxiosResponse> => { const saveUpdatedTopicData = (updatedData: Topic): Promise<AxiosResponse> => {
const jsonPatch = compare(topicDetails, updatedData); const jsonPatch = compare(topicDetails, updatedData);
@ -140,7 +163,6 @@ const TopicDetailsPage: FunctionComponent = () => {
serviceType: serviceRes.data.serviceType, serviceType: serviceRes.data.serviceType,
timestamp: 0, timestamp: 0,
}); });
setLoading(false);
}) })
.catch((err: AxiosError) => { .catch((err: AxiosError) => {
const errMsg = const errMsg =
@ -149,7 +171,8 @@ const TopicDetailsPage: FunctionComponent = () => {
variant: 'error', variant: 'error',
body: errMsg, body: errMsg,
}); });
}); })
.finally(() => setLoading(false));
}) })
.catch((err: AxiosError) => { .catch((err: AxiosError) => {
const errMsg = err.message || 'Error while fetching topic details'; const errMsg = err.message || 'Error while fetching topic details';
@ -157,6 +180,7 @@ const TopicDetailsPage: FunctionComponent = () => {
variant: 'error', variant: 'error',
body: errMsg, body: errMsg,
}); });
setLoading(false);
}); });
}; };

View File

@ -74,9 +74,36 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
component={DatasetDetailsPage} component={DatasetDetailsPage}
path={ROUTES.DATASET_DETAILS} path={ROUTES.DATASET_DETAILS}
/> />
<Route component={TopicDetailsPage} path={ROUTES.TOPIC_DETAILS} /> <Route
<Route component={DashboardDetailsPage} path={ROUTES.DASHBOARD_DETAILS} /> exact
<Route component={PipelineDetailsPage} path={ROUTES.PIPELINE_DETAILS} /> component={DatasetDetailsPage}
path={ROUTES.DATASET_DETAILS_WITH_TAB}
/>
<Route exact component={TopicDetailsPage} path={ROUTES.TOPIC_DETAILS} />
<Route
component={TopicDetailsPage}
path={ROUTES.TOPIC_DETAILS_WITH_TAB}
/>
<Route
exact
component={DashboardDetailsPage}
path={ROUTES.DASHBOARD_DETAILS}
/>
<Route
exact
component={DashboardDetailsPage}
path={ROUTES.DASHBOARD_DETAILS_WITH_TAB}
/>
<Route
exact
component={PipelineDetailsPage}
path={ROUTES.PIPELINE_DETAILS}
/>
<Route
exact
component={PipelineDetailsPage}
path={ROUTES.PIPELINE_DETAILS_WITH_TAB}
/>
<Route component={Onboarding} path={ROUTES.ONBOARDING} /> <Route component={Onboarding} path={ROUTES.ONBOARDING} />
<Route <Route
exact exact

View File

@ -0,0 +1,28 @@
export const dashboardDetailsTabs = [
{
name: 'Details',
path: 'details',
},
{
name: 'Manage',
path: 'manage',
},
];
export const getCurrentDashboardTab = (tab: string) => {
let currentTab = 1;
switch (tab) {
case 'manage':
currentTab = 2;
break;
case 'details':
default:
currentTab = 1;
break;
}
return currentTab;
};

View File

@ -0,0 +1,45 @@
export const datasetTableTabs = [
{
name: 'Schema',
path: 'schema',
},
{
name: 'Profiler',
path: 'profiler',
},
{
name: 'Lineage',
path: 'lineage',
},
{
name: 'Manage',
path: 'manage',
},
];
export const getCurrentDatasetTab = (tab: string) => {
let currentTab = 1;
switch (tab) {
case 'profiler':
currentTab = 2;
break;
case 'lineage':
currentTab = 3;
break;
case 'manage':
currentTab = 4;
break;
case 'schema':
case 'sample_data':
default:
currentTab = 1;
break;
}
return currentTab;
};

View File

@ -0,0 +1,36 @@
export const pipelineDetailsTabs = [
{
name: 'Details',
path: 'details',
},
{
name: 'Lineage',
path: 'lineage',
},
{
name: 'Manage',
path: 'manage',
},
];
export const getCurrentPipelineTab = (tab: string) => {
let currentTab = 1;
switch (tab) {
case 'lineage':
currentTab = 2;
break;
case 'manage':
currentTab = 3;
break;
case 'details':
default:
currentTab = 1;
break;
}
return currentTab;
};

View File

@ -0,0 +1,36 @@
export const topicDetailsTabs = [
{
name: 'Schema',
path: 'schema',
},
{
name: 'Config',
path: 'config',
},
{
name: 'Manage',
path: 'manage',
},
];
export const getCurrentTopicTab = (tab: string) => {
let currentTab = 1;
switch (tab) {
case 'config':
currentTab = 2;
break;
case 'manage':
currentTab = 3;
break;
case 'schema':
default:
currentTab = 1;
break;
}
return currentTab;
};