mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-11 16:31:57 +00:00
fix: lineage full screen view (#12473)
* fix: remove redirection to separate page * fix: remove unused lineage page component * fix: position full screen container * fix: add unit tests * fix: add cypress for full screen toggle * fix: lineage style issue * fix: address comments
This commit is contained in:
parent
940ab3d183
commit
6b46f4e1cb
@ -48,3 +48,38 @@ describe('Entity Details Page', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Lineage functionality', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.login();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('toggle fullscreen mode', () => {
|
||||||
|
visitEntityDetailsPage(
|
||||||
|
tableEntity.term,
|
||||||
|
tableEntity.serviceName,
|
||||||
|
tableEntity.entity
|
||||||
|
);
|
||||||
|
cy.get('[data-testid="lineage"]').click();
|
||||||
|
|
||||||
|
// Enable fullscreen
|
||||||
|
cy.get('[data-testid="full-screen"]').click();
|
||||||
|
cy.url().should('include', 'fullscreen=true');
|
||||||
|
cy.get('[data-testid="breadcrumb"]')
|
||||||
|
.should('be.visible')
|
||||||
|
.and('contain', 'Lineage');
|
||||||
|
cy.get('[data-testid="lineage-details"]').should(
|
||||||
|
'have.class',
|
||||||
|
'full-screen-lineage'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Exit fullscreen
|
||||||
|
cy.get('[data-testid="exit-full-screen"]').click();
|
||||||
|
cy.url().should('not.include', 'fullscreen=true');
|
||||||
|
cy.get('[data-testid="breadcrumb"]').should('not.contain', 'Lineage');
|
||||||
|
cy.get('[data-testid="lineage-details"]').should(
|
||||||
|
'not.have.class',
|
||||||
|
'full-screen-lineage'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -11,7 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Col, Row, Space, Table, Tabs, Typography } from 'antd';
|
import { Col, Row, Space, Table, Tabs, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import ActivityFeedProvider, {
|
import ActivityFeedProvider, {
|
||||||
@ -661,14 +661,13 @@ const DashboardDetails = ({
|
|||||||
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card className="lineage-card card-body-full w-auto border-none">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
|
entity={dashboardDetails}
|
||||||
entityType={EntityType.DASHBOARD}
|
entityType={EntityType.DASHBOARD}
|
||||||
hasEditAccess={
|
hasEditAccess={
|
||||||
dashboardPermissions.EditAll || dashboardPermissions.EditLineage
|
dashboardPermissions.EditAll || dashboardPermissions.EditLineage
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { DataAssetsHeader } from 'components/DataAssets/DataAssetsHeader/DataAss
|
|||||||
import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component';
|
import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component';
|
||||||
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
|
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
|
||||||
import SchemaEditor from 'components/schema-editor/SchemaEditor';
|
import SchemaEditor from 'components/schema-editor/SchemaEditor';
|
||||||
|
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
||||||
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
|
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
|
||||||
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
|
import TagsContainerV2 from 'components/Tag/TagsContainerV2/TagsContainerV2';
|
||||||
import { getDataModelDetailsPath, getVersionPath } from 'constants/constants';
|
import { getDataModelDetailsPath, getVersionPath } from 'constants/constants';
|
||||||
@ -313,15 +314,12 @@ const DataModelDetails = ({
|
|||||||
),
|
),
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card
|
|
||||||
className="card-body-full m-md w-auto h-60vh"
|
|
||||||
data-testid="lineage-details">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
deleted={deleted}
|
deleted={deleted}
|
||||||
|
entity={dataModelData as SourceType}
|
||||||
entityType={EntityType.DASHBOARD_DATA_MODEL}
|
entityType={EntityType.DASHBOARD_DATA_MODEL}
|
||||||
hasEditAccess={hasEditLineagePermission}
|
hasEditAccess={hasEditLineagePermission}
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -175,6 +175,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
className={classNames('custom-control-search-box', {
|
className={classNames('custom-control-search-box', {
|
||||||
'custom-control-search-box-edit-mode': isEditMode,
|
'custom-control-search-box-edit-mode': isEditMode,
|
||||||
})}
|
})}
|
||||||
|
data-testid="lineage-search"
|
||||||
filterOption={handleSearchFilterOption}
|
filterOption={handleSearchFilterOption}
|
||||||
options={nodeOptions}
|
options={nodeOptions}
|
||||||
placeholder={t('label.search-entity', {
|
placeholder={t('label.search-entity', {
|
||||||
@ -200,6 +201,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
<div className="flow-control custom-control-fit-screen-button custom-control-zoom-slide">
|
<div className="flow-control custom-control-fit-screen-button custom-control-zoom-slide">
|
||||||
<ControlButton
|
<ControlButton
|
||||||
className="custom-control-basic-button"
|
className="custom-control-basic-button"
|
||||||
|
data-testid="zoom-in-button"
|
||||||
onClick={onZoomOutHandler}>
|
onClick={onZoomOutHandler}>
|
||||||
<SVGIcons
|
<SVGIcons
|
||||||
alt="minus-icon"
|
alt="minus-icon"
|
||||||
@ -211,6 +213,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
|
|
||||||
<input
|
<input
|
||||||
className="tw-bg-body-hover"
|
className="tw-bg-body-hover"
|
||||||
|
data-testid="lineage-zoom-slider"
|
||||||
max={MAX_ZOOM_VALUE}
|
max={MAX_ZOOM_VALUE}
|
||||||
min={MIN_ZOOM_VALUE}
|
min={MIN_ZOOM_VALUE}
|
||||||
step={ZOOM_SLIDER_STEP}
|
step={ZOOM_SLIDER_STEP}
|
||||||
@ -220,6 +223,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
/>
|
/>
|
||||||
<ControlButton
|
<ControlButton
|
||||||
className="custom-control-basic-button"
|
className="custom-control-basic-button"
|
||||||
|
data-testid="zoom-out-button"
|
||||||
onClick={onZoomInHandler}>
|
onClick={onZoomInHandler}>
|
||||||
<SVGIcons
|
<SVGIcons
|
||||||
alt="plus-icon"
|
alt="plus-icon"
|
||||||
@ -233,6 +237,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
{showFitView && (
|
{showFitView && (
|
||||||
<ControlButton
|
<ControlButton
|
||||||
className="custom-control-basic-button custom-control-fit-screen-button"
|
className="custom-control-basic-button custom-control-fit-screen-button"
|
||||||
|
data-testid="fit-to-screen"
|
||||||
title={t('label.fit-to-screen')}
|
title={t('label.fit-to-screen')}
|
||||||
onClick={onFitViewHandler}>
|
onClick={onFitViewHandler}>
|
||||||
<SVGIcons alt="fit-view" icon={Icons.FITVEW} width="16" />
|
<SVGIcons alt="fit-view" icon={Icons.FITVEW} width="16" />
|
||||||
@ -241,6 +246,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
{handleFullScreenViewClick && (
|
{handleFullScreenViewClick && (
|
||||||
<ControlButton
|
<ControlButton
|
||||||
className="custom-control-basic-button custom-control-fit-screen-button"
|
className="custom-control-basic-button custom-control-fit-screen-button"
|
||||||
|
data-testid="full-screen"
|
||||||
title={t('label.full-screen')}
|
title={t('label.full-screen')}
|
||||||
onClick={handleFullScreenViewClick}>
|
onClick={handleFullScreenViewClick}>
|
||||||
<FullScreen color={PRIMERY_COLOR} height={16} width={16} />
|
<FullScreen color={PRIMERY_COLOR} height={16} width={16} />
|
||||||
@ -249,6 +255,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
{onExitFullScreenViewClick && (
|
{onExitFullScreenViewClick && (
|
||||||
<ControlButton
|
<ControlButton
|
||||||
className="custom-control-basic-button custom-control-fit-screen-button"
|
className="custom-control-basic-button custom-control-fit-screen-button"
|
||||||
|
data-testid="exit-full-screen"
|
||||||
title={t('label.exit-fit-to-screen')}
|
title={t('label.exit-fit-to-screen')}
|
||||||
onClick={onExitFullScreenViewClick}>
|
onClick={onExitFullScreenViewClick}>
|
||||||
<ExitFullScreen color={PRIMERY_COLOR} height={16} width={16} />
|
<ExitFullScreen color={PRIMERY_COLOR} height={16} width={16} />
|
||||||
@ -257,6 +264,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
|
|
||||||
<ControlButton
|
<ControlButton
|
||||||
className="custom-control-basic-button custom-control-fit-screen-button"
|
className="custom-control-basic-button custom-control-fit-screen-button"
|
||||||
|
data-testid="lineage-config"
|
||||||
disabled={isEditMode}
|
disabled={isEditMode}
|
||||||
title={t('label.setting-plural')}
|
title={t('label.setting-plural')}
|
||||||
onClick={() => setDialogVisible(true)}>
|
onClick={() => setDialogVisible(true)}>
|
||||||
@ -270,7 +278,7 @@ const CustomControls: FC<ControlProps> = ({
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
'custom-control-edit-button h-8 w-8 rounded-full p-x-xss',
|
'custom-control-edit-button h-8 w-8 rounded-full p-x-xss',
|
||||||
{
|
{
|
||||||
'bg-primary': isEditMode,
|
active: isEditMode,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
data-testid="edit-lineage"
|
data-testid="edit-lineage"
|
||||||
|
|||||||
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* 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 { fireEvent, render } from '@testing-library/react';
|
||||||
|
import { LOADING_STATE } from 'enums/common.enum';
|
||||||
|
import { MOCK_LINEAGE_DATA } from 'mocks/Lineage.mock';
|
||||||
|
import React from 'react';
|
||||||
|
import CustomControlsComponent from './CustomControls.component';
|
||||||
|
|
||||||
|
const mockFitView = jest.fn();
|
||||||
|
const mockZoomTo = jest.fn();
|
||||||
|
const mockOnOptionSelect = jest.fn();
|
||||||
|
const mockOnLineageConfigUpdate = jest.fn();
|
||||||
|
const mockOnEditLinageClick = jest.fn();
|
||||||
|
const mockOnExpandColumnClick = jest.fn();
|
||||||
|
const mockHandleFullScreenViewClick = jest.fn();
|
||||||
|
const mockOnExitFullScreenViewClick = jest.fn();
|
||||||
|
const mockOnZoomHandler = jest.fn();
|
||||||
|
const mockZoomValue = 1;
|
||||||
|
|
||||||
|
jest.mock('reactflow', () => ({
|
||||||
|
useReactFlow: () => ({
|
||||||
|
fitView: mockFitView,
|
||||||
|
zoomTo: mockZoomTo,
|
||||||
|
}),
|
||||||
|
Position: () => ({
|
||||||
|
Left: 'left',
|
||||||
|
Top: 'top',
|
||||||
|
Right: 'right',
|
||||||
|
Bottom: 'bottom',
|
||||||
|
}),
|
||||||
|
MarkerType: () => ({
|
||||||
|
Arrow: 'arrow',
|
||||||
|
ArrowClosed: 'arrowclosed',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const customProps = {
|
||||||
|
fitView: mockFitView,
|
||||||
|
zoomTo: mockZoomTo,
|
||||||
|
onOptionSelect: mockOnOptionSelect,
|
||||||
|
onLineageConfigUpdate: mockOnLineageConfigUpdate,
|
||||||
|
onEditLinageClick: mockOnEditLinageClick,
|
||||||
|
onExpandColumnClick: mockOnExpandColumnClick,
|
||||||
|
handleFullScreenViewClick: mockHandleFullScreenViewClick,
|
||||||
|
onExitFullScreenViewClick: mockOnExitFullScreenViewClick,
|
||||||
|
onZoomHandler: mockOnZoomHandler,
|
||||||
|
zoomValue: mockZoomValue,
|
||||||
|
deleted: false,
|
||||||
|
hasEditAccess: true,
|
||||||
|
isEditMode: false,
|
||||||
|
lineageData: MOCK_LINEAGE_DATA,
|
||||||
|
isColumnsExpanded: false,
|
||||||
|
loading: false,
|
||||||
|
status: LOADING_STATE.INITIAL,
|
||||||
|
lineageConfig: {
|
||||||
|
upstreamDepth: 1,
|
||||||
|
downstreamDepth: 1,
|
||||||
|
nodesPerLayer: 50,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('CustomControls', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls fitView on Fit View button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const fitViewButton = getByTestId('fit-to-screen');
|
||||||
|
fireEvent.click(fitViewButton);
|
||||||
|
|
||||||
|
expect(mockFitView).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls zoomTo with zoomInValue on Zoom In button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const zoomInButton = getByTestId('zoom-in-button');
|
||||||
|
fireEvent.click(zoomInButton);
|
||||||
|
const zoomRangeInput = getByTestId(
|
||||||
|
'lineage-zoom-slider'
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(zoomRangeInput.value).toBe('0.75');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls zoomTo with zoomOutValue on Zoom Out button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const zoomOutButton = getByTestId('zoom-out-button');
|
||||||
|
fireEvent.click(zoomOutButton);
|
||||||
|
const zoomRangeInput = getByTestId(
|
||||||
|
'lineage-zoom-slider'
|
||||||
|
) as HTMLInputElement;
|
||||||
|
|
||||||
|
expect(zoomRangeInput.value).toBe('1.25');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onEditLinageClick on Edit Lineage button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const editLineageButton = getByTestId('edit-lineage');
|
||||||
|
fireEvent.click(editLineageButton);
|
||||||
|
|
||||||
|
expect(mockOnEditLinageClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls onExpandColumnClick on Expand Column button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const expandColumnButton = getByTestId('expand-column');
|
||||||
|
fireEvent.click(expandColumnButton);
|
||||||
|
|
||||||
|
expect(mockOnExpandColumnClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls mockHandleFullScreenViewClick on Full Screen button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const fullScreenButton = getByTestId('full-screen');
|
||||||
|
fireEvent.click(fullScreenButton);
|
||||||
|
|
||||||
|
expect(mockHandleFullScreenViewClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls mockOnExitFullScreenViewClick on Exit Full Screen button click', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const exitFullScreenButton = getByTestId('exit-full-screen');
|
||||||
|
fireEvent.click(exitFullScreenButton);
|
||||||
|
|
||||||
|
expect(mockOnExitFullScreenViewClick).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show lineage config dialog on setting button click', () => {
|
||||||
|
const { getByTestId, getByRole } = render(
|
||||||
|
<CustomControlsComponent {...customProps} />
|
||||||
|
);
|
||||||
|
const settingButton = getByTestId('lineage-config');
|
||||||
|
fireEvent.click(settingButton);
|
||||||
|
const dialog = getByRole('dialog');
|
||||||
|
|
||||||
|
expect(dialog).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -11,8 +11,10 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Modal, Space } from 'antd';
|
import { Card, Modal, Space } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
||||||
import { useTourProvider } from 'components/TourProvider/TourProvider';
|
import { useTourProvider } from 'components/TourProvider/TourProvider';
|
||||||
import { mockDatasetData } from 'constants/mockTourData.constants';
|
import { mockDatasetData } from 'constants/mockTourData.constants';
|
||||||
import {
|
import {
|
||||||
@ -25,16 +27,18 @@ import {
|
|||||||
upperCase,
|
upperCase,
|
||||||
} from 'lodash';
|
} from 'lodash';
|
||||||
import { LoadingState } from 'Models';
|
import { LoadingState } from 'Models';
|
||||||
|
import Qs from 'qs';
|
||||||
import React, {
|
import React, {
|
||||||
DragEvent,
|
DragEvent,
|
||||||
FunctionComponent,
|
FunctionComponent,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useLocation, useParams } from 'react-router-dom';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
addEdge,
|
addEdge,
|
||||||
Background,
|
Background,
|
||||||
@ -52,8 +56,11 @@ import { getDataModelDetails } from 'rest/dataModelsAPI';
|
|||||||
import { getLineageByFQN } from 'rest/lineageAPI';
|
import { getLineageByFQN } from 'rest/lineageAPI';
|
||||||
import { searchData } from 'rest/miscAPI';
|
import { searchData } from 'rest/miscAPI';
|
||||||
import { getTableDetails } from 'rest/tableAPI';
|
import { getTableDetails } from 'rest/tableAPI';
|
||||||
import { getEntityLineage, getEntityName } from 'utils/EntityUtils';
|
import {
|
||||||
import { getLineageViewPath } from 'utils/RouterUtils';
|
getEntityBreadcrumbs,
|
||||||
|
getEntityLineage,
|
||||||
|
getEntityName,
|
||||||
|
} from 'utils/EntityUtils';
|
||||||
import { PAGE_SIZE } from '../../constants/constants';
|
import { PAGE_SIZE } from '../../constants/constants';
|
||||||
import {
|
import {
|
||||||
ELEMENT_DELETE_STATE,
|
ELEMENT_DELETE_STATE,
|
||||||
@ -90,7 +97,6 @@ import {
|
|||||||
getDeletedLineagePlaceholder,
|
getDeletedLineagePlaceholder,
|
||||||
getEdgeStyle,
|
getEdgeStyle,
|
||||||
getEdgeType,
|
getEdgeType,
|
||||||
getEntityLineagePath,
|
|
||||||
getEntityNodeIcon,
|
getEntityNodeIcon,
|
||||||
getLayoutedElements,
|
getLayoutedElements,
|
||||||
getLineageData,
|
getLineageData,
|
||||||
@ -150,9 +156,10 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
deleted,
|
deleted,
|
||||||
hasEditAccess,
|
hasEditAccess,
|
||||||
entityType,
|
entityType,
|
||||||
isFullScreen = false,
|
entity,
|
||||||
}: EntityLineageProp) => {
|
}: EntityLineageProp) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const location = useLocation();
|
||||||
const { isTourOpen } = useTourProvider();
|
const { isTourOpen } = useTourProvider();
|
||||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||||
const [reactFlowInstance, setReactFlowInstance] =
|
const [reactFlowInstance, setReactFlowInstance] =
|
||||||
@ -213,14 +220,34 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const params = useParams<Record<string, string>>();
|
const params = useParams<Record<string, string>>();
|
||||||
|
const queryParams = new URLSearchParams(location.search);
|
||||||
|
|
||||||
|
const isFullScreen = queryParams.get('fullscreen') === 'true';
|
||||||
const entityFQN =
|
const entityFQN =
|
||||||
params[getParamByEntityType(entityType)] ?? params['entityFQN'];
|
params[getParamByEntityType(entityType)] ?? params['entityFQN'];
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const onFullScreenClick = useCallback(() => {
|
const onFullScreenClick = useCallback(() => {
|
||||||
history.push(getLineageViewPath(entityType, entityFQN));
|
history.push({
|
||||||
|
search: Qs.stringify({ fullscreen: true }),
|
||||||
|
});
|
||||||
}, [entityType, entityFQN]);
|
}, [entityType, entityFQN]);
|
||||||
|
|
||||||
|
const breadcrumbs = useMemo(
|
||||||
|
() =>
|
||||||
|
entity
|
||||||
|
? [
|
||||||
|
...getEntityBreadcrumbs(entity, entityType),
|
||||||
|
{
|
||||||
|
name: t('label.lineage'),
|
||||||
|
url: '',
|
||||||
|
activeTitle: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
[entity]
|
||||||
|
);
|
||||||
|
|
||||||
const fetchLineageData = useCallback(
|
const fetchLineageData = useCallback(
|
||||||
async (config: LineageConfig) => {
|
async (config: LineageConfig) => {
|
||||||
if (isTourOpen) {
|
if (isTourOpen) {
|
||||||
@ -311,10 +338,9 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onExitFullScreenViewClick = useCallback(() => {
|
const onExitFullScreenViewClick = useCallback(() => {
|
||||||
const path = getEntityLineagePath(entityType, entityFQN);
|
history.push({
|
||||||
if (path !== '') {
|
search: '',
|
||||||
history.push(path);
|
});
|
||||||
}
|
|
||||||
}, [entityType, entityFQN, history]);
|
}, [entityType, entityFQN, history]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1587,6 +1613,15 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Card
|
||||||
|
className={classNames('lineage-card card-body-full w-auto border-none', {
|
||||||
|
'full-screen-lineage': isFullScreen,
|
||||||
|
})}
|
||||||
|
data-testid="lineage-details"
|
||||||
|
id="lineageDetails">
|
||||||
|
{isFullScreen && (
|
||||||
|
<TitleBreadcrumb className="p-md" titleLinks={breadcrumbs} />
|
||||||
|
)}
|
||||||
<div className="relative h-full" data-testid="lineage-container">
|
<div className="relative h-full" data-testid="lineage-container">
|
||||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
@ -1671,7 +1706,9 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<EntityInfoDrawer
|
<EntityInfoDrawer
|
||||||
isMainNode={selectedNode.name === updatedLineageData?.entity?.name}
|
isMainNode={
|
||||||
|
selectedNode.name === updatedLineageData?.entity?.name
|
||||||
|
}
|
||||||
selectedNode={selectedNode}
|
selectedNode={selectedNode}
|
||||||
show={isDrawerOpen}
|
show={isDrawerOpen}
|
||||||
onCancel={closeDrawer}
|
onCancel={closeDrawer}
|
||||||
@ -1709,6 +1746,7 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
onSelect={handlePipelineSelection}
|
onSelect={handlePipelineSelection}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
||||||
import { LoadingState } from 'Models';
|
import { LoadingState } from 'Models';
|
||||||
import { HTMLAttributes } from 'react';
|
import { HTMLAttributes } from 'react';
|
||||||
import { Edge as FlowEdge, FitViewOptions, Node } from 'reactflow';
|
import { Edge as FlowEdge, FitViewOptions, Node } from 'reactflow';
|
||||||
@ -33,6 +34,7 @@ export interface EntityLineageProp {
|
|||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
hasEditAccess?: boolean;
|
hasEditAccess?: boolean;
|
||||||
isFullScreen?: boolean;
|
isFullScreen?: boolean;
|
||||||
|
entity?: SourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Edge {
|
export interface Edge {
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render, screen } from '@testing-library/react';
|
import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { MOCK_CHILD_MAP, MOCK_LINEAGE_DATA } from 'mocks/Lineage.mock';
|
import { MOCK_CHILD_MAP, MOCK_LINEAGE_DATA } from 'mocks/Lineage.mock';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { act } from 'react-dom/test-utils';
|
import { act } from 'react-dom/test-utils';
|
||||||
@ -107,6 +107,15 @@ jest.mock('../EntityInfoDrawer/EntityInfoDrawer.component', () => {
|
|||||||
return jest.fn().mockReturnValue(<p>EntityInfoDrawerComponent</p>);
|
return jest.fn().mockReturnValue(<p>EntityInfoDrawerComponent</p>);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockPush = jest.fn();
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useHistory: () => ({
|
||||||
|
push: mockPush,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Test EntityLineage Component', () => {
|
describe('Test EntityLineage Component', () => {
|
||||||
it('Check if EntityLineage is rendering all the nodes', async () => {
|
it('Check if EntityLineage is rendering all the nodes', async () => {
|
||||||
act(() => {
|
act(() => {
|
||||||
@ -141,4 +150,52 @@ describe('Test EntityLineage Component', () => {
|
|||||||
|
|
||||||
expect(lineageContainer).not.toBeInTheDocument();
|
expect(lineageContainer).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should add fullscreen true in url on fullscreen button click', async () => {
|
||||||
|
render(<EntityLineage {...mockEntityLineageProp} />, {
|
||||||
|
wrapper: MemoryRouter,
|
||||||
|
});
|
||||||
|
|
||||||
|
const lineageContainer = await screen.findByTestId('lineage-container');
|
||||||
|
const reactFlowElement = await screen.findByTestId('rf__wrapper');
|
||||||
|
const fullscreenButton = await screen.getByTestId('full-screen');
|
||||||
|
|
||||||
|
expect(lineageContainer).toBeInTheDocument();
|
||||||
|
expect(reactFlowElement).toBeInTheDocument();
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
fireEvent.click(fullscreenButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockPush).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockPush).toHaveBeenCalledWith({
|
||||||
|
search: 'fullscreen=true',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show breadcrumbs when URL has fullscreen=true', async () => {
|
||||||
|
act(() => {
|
||||||
|
render(
|
||||||
|
<MemoryRouter
|
||||||
|
initialEntries={[
|
||||||
|
'/table/sample_data.ecommerce_db.shopify.raw_customer/lineage?fullscreen=true',
|
||||||
|
]}>
|
||||||
|
<EntityLineage {...mockEntityLineageProp} />
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const lineageContainer = await screen.findByTestId('lineage-container');
|
||||||
|
const reactFlowElement = await screen.findByTestId('rf__wrapper');
|
||||||
|
|
||||||
|
expect(lineageContainer).toBeInTheDocument();
|
||||||
|
expect(reactFlowElement).toBeInTheDocument();
|
||||||
|
|
||||||
|
const breadcrumbs = await screen.getByTestId('breadcrumb');
|
||||||
|
|
||||||
|
expect(breadcrumbs).toBeInTheDocument();
|
||||||
|
|
||||||
|
const mainRootElement = await screen.getByTestId('lineage-details');
|
||||||
|
|
||||||
|
expect(mainRootElement).toHaveClass('full-screen-lineage');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
@import url('../../styles/variables.less');
|
@import url('../../styles/variables.less');
|
||||||
|
|
||||||
@sidebar-width: 100px;
|
@lineage-sidebar-width: 100px;
|
||||||
|
|
||||||
.custom-react-flow {
|
.custom-react-flow {
|
||||||
.react-flow__node-input.selectable.selected {
|
.react-flow__node-input.selectable.selected {
|
||||||
@ -87,7 +87,7 @@
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
.ant-select.custom-control-search-box-edit-mode {
|
.ant-select.custom-control-search-box-edit-mode {
|
||||||
margin-left: @sidebar-width;
|
margin-left: @lineage-sidebar-width;
|
||||||
}
|
}
|
||||||
.custom-control-basic-button {
|
.custom-control-basic-button {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
@ -134,6 +134,9 @@
|
|||||||
display: block !important;
|
display: block !important;
|
||||||
border: 1px solid @border-color;
|
border: 1px solid @border-color;
|
||||||
background-color: @body-bg-color;
|
background-color: @body-bg-color;
|
||||||
|
&.active {
|
||||||
|
background-color: @primary-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-lineage-heading {
|
.custom-lineage-heading {
|
||||||
@ -160,7 +163,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: @sidebar-width;
|
width: @lineage-sidebar-width;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
@ -174,3 +177,11 @@
|
|||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lineage-card.full-screen-lineage {
|
||||||
|
top: @navbar-height;
|
||||||
|
left: @sidebar-width;
|
||||||
|
position: fixed !important;
|
||||||
|
width: calc(100vw - @sidebar-width);
|
||||||
|
height: calc(100vh - @navbar-height);
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Col, Row, Space, Table, Tabs, Typography } from 'antd';
|
import { Col, Row, Space, Table, Tabs, Typography } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import ActivityFeedProvider, {
|
import ActivityFeedProvider, {
|
||||||
@ -495,16 +495,13 @@ const MlModelDetail: FC<MlModelDetailProp> = ({
|
|||||||
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card
|
|
||||||
className="lineage-card card-body-full w-auto border-none"
|
|
||||||
data-testid="lineage-details">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
|
entity={mlModelDetail}
|
||||||
entityType={EntityType.MLMODEL}
|
entityType={EntityType.MLMODEL}
|
||||||
hasEditAccess={
|
hasEditAccess={
|
||||||
mlModelPermissions.EditAll || mlModelPermissions.EditLineage
|
mlModelPermissions.EditAll || mlModelPermissions.EditLineage
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -672,18 +672,14 @@ const PipelineDetails = ({
|
|||||||
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card
|
|
||||||
className="lineage-card card-body-full w-auto border-none"
|
|
||||||
data-testid="lineage-details"
|
|
||||||
id="lineageDetails">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
deleted={deleted}
|
deleted={deleted}
|
||||||
|
entity={pipelineDetails}
|
||||||
entityType={EntityType.PIPELINE}
|
entityType={EntityType.PIPELINE}
|
||||||
hasEditAccess={
|
hasEditAccess={
|
||||||
pipelinePermissions.EditAll || pipelinePermissions.EditLineage
|
pipelinePermissions.EditAll || pipelinePermissions.EditLineage
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Card, Col, Row, Space, Tabs } from 'antd';
|
import { Col, Row, Space, Tabs } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import ActivityFeedProvider, {
|
import ActivityFeedProvider, {
|
||||||
useActivityFeedProvider,
|
useActivityFeedProvider,
|
||||||
@ -397,17 +397,13 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
|
|||||||
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card
|
|
||||||
className="lineage-card card-body-full w-auto border-none"
|
|
||||||
data-testid="lineage-details"
|
|
||||||
id="lineageDetails">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
|
entity={topicDetails}
|
||||||
entityType={EntityType.TOPIC}
|
entityType={EntityType.TOPIC}
|
||||||
hasEditAccess={
|
hasEditAccess={
|
||||||
topicPermissions.EditAll || topicPermissions.EditLineage
|
topicPermissions.EditAll || topicPermissions.EditLineage
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import DataQualityPage from 'pages/DataQuality/DataQualityPage';
|
import DataQualityPage from 'pages/DataQuality/DataQualityPage';
|
||||||
import LineagePage from 'pages/LineagePage/LineagePage';
|
|
||||||
import React, { FunctionComponent, useMemo } from 'react';
|
import React, { FunctionComponent, useMemo } from 'react';
|
||||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||||
import { ROUTES } from '../../constants/constants';
|
import { ROUTES } from '../../constants/constants';
|
||||||
@ -562,11 +561,6 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
|
|||||||
|
|
||||||
<Route exact component={RequestTagsPage} path={ROUTES.REQUEST_TAGS} />
|
<Route exact component={RequestTagsPage} path={ROUTES.REQUEST_TAGS} />
|
||||||
<Route exact component={UpdateTagsPage} path={ROUTES.UPDATE_TAGS} />
|
<Route exact component={UpdateTagsPage} path={ROUTES.UPDATE_TAGS} />
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
component={LineagePage}
|
|
||||||
path={ROUTES.LINEAGE_FULL_SCREEN_VIEW}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* keep these route above the setting route always */}
|
{/* keep these route above the setting route always */}
|
||||||
<AdminProtectedRoute
|
<AdminProtectedRoute
|
||||||
|
|||||||
@ -286,7 +286,6 @@ export const ROUTES = {
|
|||||||
PROFILER_DASHBOARD: `/profiler-dashboard/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ENTITY_TYPE_FQN}`,
|
PROFILER_DASHBOARD: `/profiler-dashboard/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ENTITY_TYPE_FQN}`,
|
||||||
PROFILER_DASHBOARD_WITH_TAB: `/profiler-dashboard/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ENTITY_TYPE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
|
PROFILER_DASHBOARD_WITH_TAB: `/profiler-dashboard/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ENTITY_TYPE_FQN}/${PLACEHOLDER_ROUTE_TAB}`,
|
||||||
ADD_DATA_QUALITY_TEST_CASE: `/data-quality-test/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ENTITY_TYPE_FQN}`,
|
ADD_DATA_QUALITY_TEST_CASE: `/data-quality-test/${PLACEHOLDER_DASHBOARD_TYPE}/${PLACEHOLDER_ENTITY_TYPE_FQN}`,
|
||||||
LINEAGE_FULL_SCREEN_VIEW: `/lineage-view/${PLACEHOLDER_ROUTE_ENTITY_TYPE}/${PLACEHOLDER_ROUTE_ENTITY_FQN}`,
|
|
||||||
|
|
||||||
// Query Routes
|
// Query Routes
|
||||||
QUERY_FULL_SCREEN_VIEW: `/query-view/${PLACEHOLDER_ROUTE_TABLE_FQN}/${PLACEHOLDER_ROUTE_QUERY_ID}`,
|
QUERY_FULL_SCREEN_VIEW: `/query-view/${PLACEHOLDER_ROUTE_TABLE_FQN}/${PLACEHOLDER_ROUTE_QUERY_ID}`,
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Card, Col, Row, Space, Tabs } from 'antd';
|
import { Col, Row, Space, Tabs } from 'antd';
|
||||||
import AppState from 'AppState';
|
import AppState from 'AppState';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import ActivityFeedProvider, {
|
import ActivityFeedProvider, {
|
||||||
@ -27,13 +27,6 @@ import ContainerDataModel from 'components/ContainerDetail/ContainerDataModel/Co
|
|||||||
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
||||||
import { DataAssetsHeader } from 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
|
import { DataAssetsHeader } from 'components/DataAssets/DataAssetsHeader/DataAssetsHeader.component';
|
||||||
import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component';
|
import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component';
|
||||||
import {
|
|
||||||
Edge,
|
|
||||||
EdgeData,
|
|
||||||
LeafNodes,
|
|
||||||
LineagePos,
|
|
||||||
LoadingNodeState,
|
|
||||||
} from 'components/EntityLineage/EntityLineage.interface';
|
|
||||||
import Loader from 'components/Loader/Loader';
|
import Loader from 'components/Loader/Loader';
|
||||||
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
|
import { EntityName } from 'components/Modals/EntityNameModal/EntityNameModal.interface';
|
||||||
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
import { usePermissionProvider } from 'components/PermissionProvider/PermissionProvider';
|
||||||
@ -50,8 +43,6 @@ import { EntityTabs, EntityType } from 'enums/entity.enum';
|
|||||||
import { compare } from 'fast-json-patch';
|
import { compare } from 'fast-json-patch';
|
||||||
import { CreateThread, ThreadType } from 'generated/api/feed/createThread';
|
import { CreateThread, ThreadType } from 'generated/api/feed/createThread';
|
||||||
import { Container } from 'generated/entity/data/container';
|
import { Container } from 'generated/entity/data/container';
|
||||||
import { EntityLineage } from 'generated/type/entityLineage';
|
|
||||||
import { EntityReference } from 'generated/type/entityReference';
|
|
||||||
import { Include } from 'generated/type/include';
|
import { Include } from 'generated/type/include';
|
||||||
import { LabelType, State, TagLabel, TagSource } from 'generated/type/tagLabel';
|
import { LabelType, State, TagLabel, TagSource } from 'generated/type/tagLabel';
|
||||||
import { EntityFieldThreadCount } from 'interface/feed.interface';
|
import { EntityFieldThreadCount } from 'interface/feed.interface';
|
||||||
@ -62,8 +53,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { postThread } from 'rest/feedsAPI';
|
import { postThread } from 'rest/feedsAPI';
|
||||||
import { getLineageByFQN } from 'rest/lineageAPI';
|
|
||||||
import { addLineage, deleteLineageEdge } from 'rest/miscAPI';
|
|
||||||
import {
|
import {
|
||||||
addContainerFollower,
|
addContainerFollower,
|
||||||
getContainerByName,
|
getContainerByName,
|
||||||
@ -79,14 +68,9 @@ import {
|
|||||||
refreshPage,
|
refreshPage,
|
||||||
sortTagsCaseInsensitive,
|
sortTagsCaseInsensitive,
|
||||||
} from 'utils/CommonUtils';
|
} from 'utils/CommonUtils';
|
||||||
import {
|
import { getEntityName, getEntityThreadLink } from 'utils/EntityUtils';
|
||||||
getEntityLineage,
|
|
||||||
getEntityName,
|
|
||||||
getEntityThreadLink,
|
|
||||||
} from 'utils/EntityUtils';
|
|
||||||
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
|
import { getEntityFieldThreadCounts } from 'utils/FeedUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
||||||
import { getLineageViewPath } from 'utils/RouterUtils';
|
|
||||||
import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
|
import { getTagsWithoutTier, getTierTags } from 'utils/TableUtils';
|
||||||
import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
|
import { showErrorToast, showSuccessToast } from 'utils/ToastUtils';
|
||||||
|
|
||||||
@ -103,7 +87,6 @@ const ContainerPage = () => {
|
|||||||
const [isChildrenLoading, setIsChildrenLoading] = useState<boolean>(false);
|
const [isChildrenLoading, setIsChildrenLoading] = useState<boolean>(false);
|
||||||
const [hasError, setHasError] = useState<boolean>(false);
|
const [hasError, setHasError] = useState<boolean>(false);
|
||||||
const [isEditDescription, setIsEditDescription] = useState<boolean>(false);
|
const [isEditDescription, setIsEditDescription] = useState<boolean>(false);
|
||||||
const [isLineageLoading, setIsLineageLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const [containerData, setContainerData] = useState<Container>();
|
const [containerData, setContainerData] = useState<Container>();
|
||||||
const [containerChildrenData, setContainerChildrenData] = useState<
|
const [containerChildrenData, setContainerChildrenData] = useState<
|
||||||
@ -111,14 +94,6 @@ const ContainerPage = () => {
|
|||||||
>([]);
|
>([]);
|
||||||
const [containerPermissions, setContainerPermissions] =
|
const [containerPermissions, setContainerPermissions] =
|
||||||
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
|
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
|
||||||
const [entityLineage, setEntityLineage] = useState<EntityLineage>(
|
|
||||||
{} as EntityLineage
|
|
||||||
);
|
|
||||||
const [leafNodes, setLeafNodes] = useState<LeafNodes>({} as LeafNodes);
|
|
||||||
const [isNodeLoading, setNodeLoading] = useState<LoadingNodeState>({
|
|
||||||
id: undefined,
|
|
||||||
state: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [feedCount, setFeedCount] = useState<number>(0);
|
const [feedCount, setFeedCount] = useState<number>(0);
|
||||||
const [entityFieldThreadCount, setEntityFieldThreadCount] = useState<
|
const [entityFieldThreadCount, setEntityFieldThreadCount] = useState<
|
||||||
@ -170,22 +145,6 @@ const ContainerPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchLineageData = async (containerFQN: string) => {
|
|
||||||
setIsLineageLoading(true);
|
|
||||||
try {
|
|
||||||
const response = await getLineageByFQN(
|
|
||||||
containerFQN,
|
|
||||||
EntityType.CONTAINER
|
|
||||||
);
|
|
||||||
|
|
||||||
setEntityLineage(response);
|
|
||||||
} catch (error) {
|
|
||||||
showErrorToast(error as AxiosError);
|
|
||||||
} finally {
|
|
||||||
setIsLineageLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchResourcePermission = async (containerFQN: string) => {
|
const fetchResourcePermission = async (containerFQN: string) => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
@ -431,67 +390,6 @@ const ContainerPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Lineage handlers
|
|
||||||
const handleAddLineage = async (edge: Edge) => {
|
|
||||||
try {
|
|
||||||
await addLineage(edge);
|
|
||||||
} catch (error) {
|
|
||||||
showErrorToast(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRemoveLineage = async (data: EdgeData) => {
|
|
||||||
try {
|
|
||||||
await deleteLineageEdge(
|
|
||||||
data.fromEntity,
|
|
||||||
data.fromId,
|
|
||||||
data.toEntity,
|
|
||||||
data.toId
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
showErrorToast(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSetLeafNode = (val: EntityLineage, pos: LineagePos) => {
|
|
||||||
if (pos === 'to' && val.downstreamEdges?.length === 0) {
|
|
||||||
setLeafNodes((prev) => ({
|
|
||||||
...prev,
|
|
||||||
downStreamNode: [...(prev.downStreamNode ?? []), val.entity.id],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
if (pos === 'from' && val.upstreamEdges?.length === 0) {
|
|
||||||
setLeafNodes((prev) => ({
|
|
||||||
...prev,
|
|
||||||
upStreamNode: [...(prev.upStreamNode ?? []), val.entity.id],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLoadLineageNode = async (
|
|
||||||
node: EntityReference,
|
|
||||||
pos: LineagePos
|
|
||||||
) => {
|
|
||||||
setNodeLoading({ id: node.id, state: true });
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await getLineageByFQN(
|
|
||||||
node.fullyQualifiedName ?? '',
|
|
||||||
node.type
|
|
||||||
);
|
|
||||||
handleSetLeafNode(response, pos);
|
|
||||||
setEntityLineage(getEntityLineage(entityLineage, response, pos));
|
|
||||||
setTimeout(() => {
|
|
||||||
setNodeLoading((prev) => ({ ...prev, state: false }));
|
|
||||||
}, 500);
|
|
||||||
} catch (error) {
|
|
||||||
showErrorToast(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFullScreenClick = () =>
|
|
||||||
history.push(getLineageViewPath(EntityType.CONTAINER, containerName));
|
|
||||||
|
|
||||||
const handleExtensionUpdate = async (updatedContainer: Container) => {
|
const handleExtensionUpdate = async (updatedContainer: Container) => {
|
||||||
try {
|
try {
|
||||||
const response = await handleUpdateContainerData(updatedContainer);
|
const response = await handleUpdateContainerData(updatedContainer);
|
||||||
@ -691,24 +589,11 @@ const ContainerPage = () => {
|
|||||||
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card className="lineage-card card-body-full m-md w-auto">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
addLineageHandler={handleAddLineage}
|
entity={containerData}
|
||||||
deleted={deleted}
|
|
||||||
entityLineage={entityLineage}
|
|
||||||
entityLineageHandler={(lineage: EntityLineage) =>
|
|
||||||
setEntityLineage(lineage)
|
|
||||||
}
|
|
||||||
entityType={EntityType.CONTAINER}
|
entityType={EntityType.CONTAINER}
|
||||||
hasEditAccess={hasEditLineagePermission}
|
hasEditAccess={hasEditLineagePermission}
|
||||||
isLoading={isLineageLoading}
|
|
||||||
isNodeLoading={isNodeLoading}
|
|
||||||
lineageLeafNodes={leafNodes}
|
|
||||||
loadNodeHandler={handleLoadLineageNode}
|
|
||||||
removeLineageHandler={handleRemoveLineage}
|
|
||||||
onFullScreenClick={handleFullScreenClick}
|
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -745,24 +630,16 @@ const ContainerPage = () => {
|
|||||||
hasEditCustomFieldsPermission,
|
hasEditCustomFieldsPermission,
|
||||||
deleted,
|
deleted,
|
||||||
owner,
|
owner,
|
||||||
isNodeLoading,
|
|
||||||
leafNodes,
|
|
||||||
isLineageLoading,
|
|
||||||
isChildrenLoading,
|
isChildrenLoading,
|
||||||
entityFieldThreadCount,
|
entityFieldThreadCount,
|
||||||
tags,
|
tags,
|
||||||
entityLineage,
|
|
||||||
feedCount,
|
feedCount,
|
||||||
containerChildrenData,
|
containerChildrenData,
|
||||||
handleAddLineage,
|
|
||||||
handleUpdateDataModel,
|
handleUpdateDataModel,
|
||||||
handleUpdateDescription,
|
handleUpdateDescription,
|
||||||
getEntityFieldThreadCounts,
|
getEntityFieldThreadCounts,
|
||||||
handleTagSelection,
|
handleTagSelection,
|
||||||
onThreadLinkSelect,
|
onThreadLinkSelect,
|
||||||
handleLoadLineageNode,
|
|
||||||
handleRemoveLineage,
|
|
||||||
handleFullScreenClick,
|
|
||||||
handleExtensionUpdate,
|
handleExtensionUpdate,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@ -779,9 +656,6 @@ const ContainerPage = () => {
|
|||||||
}, [containerName]);
|
}, [containerName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (tab === EntityTabs.LINEAGE) {
|
|
||||||
fetchLineageData(containerName);
|
|
||||||
}
|
|
||||||
if (tab === EntityTabs.CHILDREN) {
|
if (tab === EntityTabs.CHILDREN) {
|
||||||
fetchContainerChildren(containerName);
|
fetchContainerChildren(containerName);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 { AxiosError } from 'axios';
|
|
||||||
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
|
||||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
|
||||||
import PageLayoutV1 from 'components/containers/PageLayoutV1';
|
|
||||||
import EntityLineageComponent from 'components/EntityLineage/EntityLineage.component';
|
|
||||||
import { Container } from 'generated/entity/data/container';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { getDashboardByFqn } from 'rest/dashboardAPI';
|
|
||||||
import { getMlModelByFQN } from 'rest/mlModelAPI';
|
|
||||||
import { getPipelineByFqn } from 'rest/pipelineAPI';
|
|
||||||
import { getContainerByName } from 'rest/storageAPI';
|
|
||||||
import { getTableDetailsByFQN } from 'rest/tableAPI';
|
|
||||||
import { getTopicByFqn } from 'rest/topicsAPI';
|
|
||||||
import {
|
|
||||||
getContainerDetailPath,
|
|
||||||
getDashboardDetailsPath,
|
|
||||||
getMlModelPath,
|
|
||||||
getPipelineDetailsPath,
|
|
||||||
getTableTabPath,
|
|
||||||
getTopicDetailsPath,
|
|
||||||
} from '../../constants/constants';
|
|
||||||
import { EntityTabs, EntityType } from '../../enums/entity.enum';
|
|
||||||
import { Dashboard } from '../../generated/entity/data/dashboard';
|
|
||||||
import { Mlmodel } from '../../generated/entity/data/mlmodel';
|
|
||||||
import { Pipeline } from '../../generated/entity/data/pipeline';
|
|
||||||
import { Topic } from '../../generated/entity/data/topic';
|
|
||||||
import { getEntityBreadcrumbs, getEntityName } from '../../utils/EntityUtils';
|
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
|
||||||
import './lineagePage.style.less';
|
|
||||||
|
|
||||||
const LineagePage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { entityType, entityFQN } =
|
|
||||||
useParams<{ entityType: EntityType; entityFQN: string }>();
|
|
||||||
|
|
||||||
const [titleBreadcrumb, setTitleBreadcrumb] = useState<
|
|
||||||
TitleBreadcrumbProps['titleLinks']
|
|
||||||
>([]);
|
|
||||||
|
|
||||||
const updateBreadcrumb = (
|
|
||||||
apiRes: Topic | Dashboard | Pipeline | Mlmodel | Container,
|
|
||||||
currentEntityPath: string,
|
|
||||||
entityType: EntityType
|
|
||||||
) => {
|
|
||||||
setTitleBreadcrumb([
|
|
||||||
...getEntityBreadcrumbs(apiRes, entityType),
|
|
||||||
{
|
|
||||||
name: getEntityName(apiRes),
|
|
||||||
url: currentEntityPath,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('label.lineage'),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchEntityDetails = async () => {
|
|
||||||
try {
|
|
||||||
switch (entityType) {
|
|
||||||
case EntityType.TABLE:
|
|
||||||
{
|
|
||||||
const tableRes = await getTableDetailsByFQN(entityFQN, '');
|
|
||||||
setTitleBreadcrumb([
|
|
||||||
...getEntityBreadcrumbs(tableRes, EntityType.TABLE),
|
|
||||||
{
|
|
||||||
name: getEntityName(tableRes),
|
|
||||||
url: getTableTabPath(entityFQN, EntityTabs.LINEAGE),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('label.lineage'),
|
|
||||||
url: '',
|
|
||||||
activeTitle: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EntityType.TOPIC:
|
|
||||||
{
|
|
||||||
const topicRes = await getTopicByFqn(entityFQN, '');
|
|
||||||
updateBreadcrumb(
|
|
||||||
topicRes,
|
|
||||||
getTopicDetailsPath(entityFQN, EntityTabs.LINEAGE),
|
|
||||||
EntityType.TOPIC
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EntityType.DASHBOARD:
|
|
||||||
{
|
|
||||||
const dashboardRes = await getDashboardByFqn(entityFQN, '');
|
|
||||||
updateBreadcrumb(
|
|
||||||
dashboardRes,
|
|
||||||
getDashboardDetailsPath(entityFQN, EntityTabs.LINEAGE),
|
|
||||||
EntityType.DASHBOARD
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EntityType.PIPELINE:
|
|
||||||
{
|
|
||||||
const pipelineRes = await getPipelineByFqn(entityFQN, '');
|
|
||||||
updateBreadcrumb(
|
|
||||||
pipelineRes,
|
|
||||||
getPipelineDetailsPath(entityFQN, EntityTabs.LINEAGE),
|
|
||||||
EntityType.PIPELINE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EntityType.MLMODEL:
|
|
||||||
{
|
|
||||||
const mlmodelRes = await getMlModelByFQN(entityFQN, '');
|
|
||||||
updateBreadcrumb(
|
|
||||||
mlmodelRes,
|
|
||||||
getMlModelPath(entityFQN, EntityTabs.LINEAGE),
|
|
||||||
EntityType.MLMODEL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EntityType.CONTAINER:
|
|
||||||
{
|
|
||||||
const containerRes = await getContainerByName(entityFQN, '');
|
|
||||||
updateBreadcrumb(
|
|
||||||
containerRes,
|
|
||||||
getContainerDetailPath(entityFQN, EntityTabs.LINEAGE),
|
|
||||||
EntityType.CONTAINER
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
showErrorToast(
|
|
||||||
error as AxiosError,
|
|
||||||
t('server.entity-fetch-error', {
|
|
||||||
entity: entityType,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (entityFQN && entityType) {
|
|
||||||
fetchEntityDetails();
|
|
||||||
}
|
|
||||||
}, [entityFQN, entityType]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageLayoutV1 className="p-x-lg" pageTitle={t('label.lineage')}>
|
|
||||||
<div className="lineage-page-container page-container">
|
|
||||||
<TitleBreadcrumb titleLinks={titleBreadcrumb} />
|
|
||||||
<EntityLineageComponent
|
|
||||||
hasEditAccess
|
|
||||||
isFullScreen
|
|
||||||
entityType={entityType}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</PageLayoutV1>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default LineagePage;
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.lineage-page-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.ant-card-body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,7 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Card, Col, Row, Space, Tabs, Typography } from 'antd';
|
import { Col, Row, Space, Tabs, Typography } from 'antd';
|
||||||
import { AxiosError } from 'axios';
|
import { AxiosError } from 'axios';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import ActivityFeedProvider, {
|
import ActivityFeedProvider, {
|
||||||
@ -35,6 +35,7 @@ import {
|
|||||||
} from 'components/PermissionProvider/PermissionProvider.interface';
|
} from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
import SampleDataTableComponent from 'components/SampleDataTable/SampleDataTable.component';
|
import SampleDataTableComponent from 'components/SampleDataTable/SampleDataTable.component';
|
||||||
import SchemaTab from 'components/SchemaTab/SchemaTab.component';
|
import SchemaTab from 'components/SchemaTab/SchemaTab.component';
|
||||||
|
import { SourceType } from 'components/searched-data/SearchedData.interface';
|
||||||
import TableProfilerV1 from 'components/TableProfiler/TableProfilerV1';
|
import TableProfilerV1 from 'components/TableProfiler/TableProfilerV1';
|
||||||
import TableQueries from 'components/TableQueries/TableQueries';
|
import TableQueries from 'components/TableQueries/TableQueries';
|
||||||
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
|
import TabsLabel from 'components/TabsLabel/TabsLabel.component';
|
||||||
@ -624,17 +625,14 @@ const TableDetailsPageV1 = () => {
|
|||||||
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
label: <TabsLabel id={EntityTabs.LINEAGE} name={t('label.lineage')} />,
|
||||||
key: EntityTabs.LINEAGE,
|
key: EntityTabs.LINEAGE,
|
||||||
children: (
|
children: (
|
||||||
<Card
|
|
||||||
className="lineage-card card-body-full w-auto border-none"
|
|
||||||
id="lineageDetails">
|
|
||||||
<EntityLineageComponent
|
<EntityLineageComponent
|
||||||
deleted={tableDetails?.deleted}
|
deleted={tableDetails?.deleted}
|
||||||
|
entity={tableDetails as SourceType}
|
||||||
entityType={EntityType.TABLE}
|
entityType={EntityType.TABLE}
|
||||||
hasEditAccess={
|
hasEditAccess={
|
||||||
tablePermissions.EditAll || tablePermissions.EditLineage
|
tablePermissions.EditAll || tablePermissions.EditLineage
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Card>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -78,6 +78,9 @@
|
|||||||
@grey-bg-with-alpha: #7575751a;
|
@grey-bg-with-alpha: #7575751a;
|
||||||
@btn-shadow: none;
|
@btn-shadow: none;
|
||||||
|
|
||||||
|
@navbar-height: 64px;
|
||||||
|
@sidebar-width: 60px;
|
||||||
|
|
||||||
// Sizing
|
// Sizing
|
||||||
@page-height: calc(100vh - 64px);
|
@page-height: calc(100vh - 64px);
|
||||||
@left-side-panel-width: 230px;
|
@left-side-panel-width: 230px;
|
||||||
|
|||||||
@ -25,8 +25,6 @@ import {
|
|||||||
PLACEHOLDER_ENTITY_TYPE_FQN,
|
PLACEHOLDER_ENTITY_TYPE_FQN,
|
||||||
PLACEHOLDER_GLOSSARY_NAME,
|
PLACEHOLDER_GLOSSARY_NAME,
|
||||||
PLACEHOLDER_GLOSSARY_TERMS_FQN,
|
PLACEHOLDER_GLOSSARY_TERMS_FQN,
|
||||||
PLACEHOLDER_ROUTE_ENTITY_FQN,
|
|
||||||
PLACEHOLDER_ROUTE_ENTITY_TYPE,
|
|
||||||
PLACEHOLDER_ROUTE_FQN,
|
PLACEHOLDER_ROUTE_FQN,
|
||||||
PLACEHOLDER_ROUTE_INGESTION_FQN,
|
PLACEHOLDER_ROUTE_INGESTION_FQN,
|
||||||
PLACEHOLDER_ROUTE_INGESTION_TYPE,
|
PLACEHOLDER_ROUTE_INGESTION_TYPE,
|
||||||
@ -48,7 +46,7 @@ import {
|
|||||||
GlobalSettingsMenuCategory,
|
GlobalSettingsMenuCategory,
|
||||||
} from '../constants/GlobalSettings.constants';
|
} from '../constants/GlobalSettings.constants';
|
||||||
import { arrServiceTypes } from '../constants/Services.constant';
|
import { arrServiceTypes } from '../constants/Services.constant';
|
||||||
import { EntityAction, EntityType } from '../enums/entity.enum';
|
import { EntityAction } from '../enums/entity.enum';
|
||||||
import { PipelineType } from '../generated/api/services/ingestionPipelines/createIngestionPipeline';
|
import { PipelineType } from '../generated/api/services/ingestionPipelines/createIngestionPipeline';
|
||||||
import { getServiceRouteFromServiceType } from './ServiceUtils';
|
import { getServiceRouteFromServiceType } from './ServiceUtils';
|
||||||
import { getEncodedFqn } from './StringsUtils';
|
import { getEncodedFqn } from './StringsUtils';
|
||||||
@ -395,16 +393,6 @@ export const getLogEntityPath = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLineageViewPath = (entity: EntityType, fqn: string) => {
|
|
||||||
let path = ROUTES.LINEAGE_FULL_SCREEN_VIEW;
|
|
||||||
|
|
||||||
path = path
|
|
||||||
.replace(PLACEHOLDER_ROUTE_ENTITY_TYPE, entity)
|
|
||||||
.replace(PLACEHOLDER_ROUTE_ENTITY_FQN, fqn);
|
|
||||||
|
|
||||||
return path;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getGlossaryPathWithAction = (
|
export const getGlossaryPathWithAction = (
|
||||||
fqn: string,
|
fqn: string,
|
||||||
action: EntityAction
|
action: EntityAction
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user