Fix#16404 - Show Node level lineage by default (#16445)

* default to node layer

* update cypress

* code cleanup

* fix cypress
This commit is contained in:
Karan Hotchandani 2024-05-29 12:10:02 +05:30 committed by GitHub
parent 0abd3ca5fe
commit f0cda8464f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 96 additions and 95 deletions

View File

@ -199,15 +199,18 @@ const addPipelineBetweenNodes = (
} }
}; };
const expandCols = (nodeFqn, hasShowMore) => { const activateColumnLayer = () => {
cy.get( cy.get('[data-testid="lineage-layer-btn"]').click();
`[data-testid="lineage-node-${nodeFqn}"] [data-testid="expand-cols-btn"]` cy.get('[data-testid="lineage-layer-column-btn"]').click();
).click({ force: true }); };
if (hasShowMore) {
cy.get( const verifyColumnLayerInactive = () => {
`[data-testid="lineage-node-${nodeFqn}"] [data-testid="show-more-cols-btn"]` cy.get('[data-testid="lineage-layer-btn"]').click(); // Open Layer popover
).click({ force: true }); cy.get('[data-testid="lineage-layer-column-btn"]').should(
} 'not.have.class',
'active'
);
cy.get('[data-testid="lineage-layer-btn"]').click(); // Close Layer popover
}; };
const addColumnLineage = (fromNode, toNode, exitEditMode = true) => { const addColumnLineage = (fromNode, toNode, exitEditMode = true) => {
@ -243,6 +246,7 @@ describe('Lineage verification', { tags: 'DataAssets' }, () => {
}); });
cy.get('[data-testid="lineage"]').click(); cy.get('[data-testid="lineage"]').click();
verifyColumnLayerInactive();
cy.get('[data-testid="edit-lineage"]').click(); cy.get('[data-testid="edit-lineage"]').click();
performZoomOut(); performZoomOut();
@ -329,6 +333,7 @@ describe('Lineage verification', { tags: 'DataAssets' }, () => {
const targetEntity = LINEAGE_ITEMS[i]; const targetEntity = LINEAGE_ITEMS[i];
if (targetEntity.columns.length > 0) { if (targetEntity.columns.length > 0) {
addPipelineBetweenNodes(sourceEntity, targetEntity); addPipelineBetweenNodes(sourceEntity, targetEntity);
activateColumnLayer();
// Add column lineage // Add column lineage
addColumnLineage(sourceEntity, targetEntity); addColumnLineage(sourceEntity, targetEntity);
cy.get('[data-testid="edit-lineage"]').click(); cy.get('[data-testid="edit-lineage"]').click();

View File

@ -20,7 +20,6 @@ import CustomControlsComponent from './CustomControls.component';
const mockOnOptionSelect = jest.fn(); const mockOnOptionSelect = jest.fn();
const mockOnLineageConfigUpdate = jest.fn(); const mockOnLineageConfigUpdate = jest.fn();
const mockOnEditLineageClick = jest.fn(); const mockOnEditLineageClick = jest.fn();
const mockOnExpandColumnClick = jest.fn();
const mockHandleFullScreenViewClick = jest.fn(); const mockHandleFullScreenViewClick = jest.fn();
const mockOnExitFullScreenViewClick = jest.fn(); const mockOnExitFullScreenViewClick = jest.fn();
const mockOnZoomHandler = jest.fn(); const mockOnZoomHandler = jest.fn();
@ -50,7 +49,6 @@ jest.mock('reactflow', () => ({
jest.mock('../../../context/LineageProvider/LineageProvider', () => ({ jest.mock('../../../context/LineageProvider/LineageProvider', () => ({
useLineageProvider: jest.fn().mockImplementation(() => ({ useLineageProvider: jest.fn().mockImplementation(() => ({
toggleColumnView: mockOnExpandColumnClick,
onLineageEditClick: mockOnEditLineageClick, onLineageEditClick: mockOnEditLineageClick,
onExportClick: mockOnExportClick, onExportClick: mockOnExportClick,
activeLayer: [LineageLayerView.COLUMN], activeLayer: [LineageLayerView.COLUMN],

View File

@ -87,7 +87,6 @@ jest.mock('../../../context/LineageProvider/LineageProvider', () => ({
downstreamEdges: [], downstreamEdges: [],
}, },
activeLayer: [LineageLayerView.COLUMN], activeLayer: [LineageLayerView.COLUMN],
expandedNodes: [],
fetchPipelineStatus: jest.fn(), fetchPipelineStatus: jest.fn(),
onColumnClick: onMockColumnClick, onColumnClick: onMockColumnClick,
})), })),

View File

@ -61,7 +61,6 @@ jest.mock('../../../../context/LineageProvider/LineageProvider', () => ({
downstreamEdges: [], downstreamEdges: [],
}, },
activeLayer: [], activeLayer: [],
expandedNodes: [],
fetchPipelineStatus: jest.fn(), fetchPipelineStatus: jest.fn(),
onColumnClick: onMockColumnClick, onColumnClick: onMockColumnClick,
onUpdateLayerView: onMockUpdateLayerView, onUpdateLayerView: onMockUpdateLayerView,

View File

@ -24,7 +24,7 @@ import { getEntityIcon } from '../../../../utils/TableUtils';
import './lineage-layers.less'; import './lineage-layers.less';
const LineageLayers = () => { const LineageLayers = () => {
const { activeLayer, onUpdateLayerView } = useLineageProvider(); const { activeLayer, onUpdateLayerView, isEditMode } = useLineageProvider();
const onButtonClick = (value: LineageLayerView) => { const onButtonClick = (value: LineageLayerView) => {
const index = activeLayer.indexOf(value); const index = activeLayer.indexOf(value);
@ -43,6 +43,7 @@ const LineageLayers = () => {
className={classNames('lineage-layer-button h-15', { className={classNames('lineage-layer-button h-15', {
active: activeLayer.includes(LineageLayerView.COLUMN), active: activeLayer.includes(LineageLayerView.COLUMN),
})} })}
data-testid="lineage-layer-column-btn"
onClick={() => onButtonClick(LineageLayerView.COLUMN)}> onClick={() => onButtonClick(LineageLayerView.COLUMN)}>
<div className="lineage-layer-btn"> <div className="lineage-layer-btn">
<div className="layer-icon"> <div className="layer-icon">
@ -59,6 +60,7 @@ const LineageLayers = () => {
LineageLayerView.DATA_OBSERVARABILITY LineageLayerView.DATA_OBSERVARABILITY
), ),
})} })}
data-testid="lineage-layer-observability-btn"
onClick={() => onClick={() =>
onButtonClick(LineageLayerView.DATA_OBSERVARABILITY) onButtonClick(LineageLayerView.DATA_OBSERVARABILITY)
}> }>
@ -78,7 +80,9 @@ const LineageLayers = () => {
trigger="click"> trigger="click">
<Button <Button
ghost ghost
className="layers-btn h-15" className={classNames('layers-btn h-15', {
'layers-btn-edit-mode': isEditMode,
})}
data-testid="lineage-layer-btn" data-testid="lineage-layer-btn"
type="primary"> type="primary">
<div className="lineage-layer-btn"> <div className="lineage-layer-btn">

View File

@ -51,3 +51,7 @@
.layers-btn { .layers-btn {
background-color: @white !important; background-color: @white !important;
} }
.layers-btn-edit-mode {
margin-left: @lineage-sidebar-width;
}

View File

@ -13,7 +13,6 @@
@import (reference) url('../../../styles/variables.less'); @import (reference) url('../../../styles/variables.less');
@lineage-sidebar-width: 110px;
@lineage-breadcrumb-panel: 50px; @lineage-breadcrumb-panel: 50px;
@lineage-toolbar-height: 54px; @lineage-toolbar-height: 54px;

View File

@ -156,6 +156,9 @@ const Lineage = ({
deleteKeyCode={null} deleteKeyCode={null}
edgeTypes={customEdges} edgeTypes={customEdges}
edges={edges} edges={edges}
fitViewOptions={{
padding: 48,
}}
maxZoom={MAX_ZOOM_VALUE} maxZoom={MAX_ZOOM_VALUE}
minZoom={MIN_ZOOM_VALUE} minZoom={MIN_ZOOM_VALUE}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}

View File

@ -19,7 +19,7 @@ import { SearchIndex } from '../enums/search.enum';
import { Source } from '../generated/type/entityLineage'; import { Source } from '../generated/type/entityLineage';
export const FOREIGN_OBJECT_SIZE = 40; export const FOREIGN_OBJECT_SIZE = 40;
export const ZOOM_VALUE = 0.75; export const ZOOM_VALUE = 0.65;
export const MIN_ZOOM_VALUE = 0.1; export const MIN_ZOOM_VALUE = 0.1;
export const MAX_ZOOM_VALUE = 2.5; export const MAX_ZOOM_VALUE = 2.5;
export const ZOOM_SLIDER_STEP = 0.1; export const ZOOM_SLIDER_STEP = 0.1;

View File

@ -53,7 +53,6 @@ export interface LineageContextType {
reactFlowInstance?: ReactFlowInstance; reactFlowInstance?: ReactFlowInstance;
nodes: Node[]; nodes: Node[];
edges: Edge[]; edges: Edge[];
expandedNodes: string[];
tracedNodes: string[]; tracedNodes: string[];
tracedColumns: string[]; tracedColumns: string[];
lineageConfig: LineageConfig; lineageConfig: LineageConfig;
@ -67,7 +66,6 @@ export interface LineageContextType {
selectedNode: SourceType; selectedNode: SourceType;
upstreamDownstreamData: UpstreamDownstreamData; upstreamDownstreamData: UpstreamDownstreamData;
selectedColumn: string; selectedColumn: string;
expandAllColumns: boolean;
activeLayer: LineageLayerView[]; activeLayer: LineageLayerView[];
onInitReactFlow: (reactFlowInstance: ReactFlowInstance) => void; onInitReactFlow: (reactFlowInstance: ReactFlowInstance) => void;
onPaneClick: () => void; onPaneClick: () => void;
@ -83,7 +81,6 @@ export interface LineageContextType {
onNodeCollapse: (node: Node | NodeProps, direction: EdgeTypeEnum) => void; onNodeCollapse: (node: Node | NodeProps, direction: EdgeTypeEnum) => void;
onNodesChange: (changes: NodeChange[]) => void; onNodesChange: (changes: NodeChange[]) => void;
onEdgesChange: (changes: EdgeChange[]) => void; onEdgesChange: (changes: EdgeChange[]) => void;
toggleColumnView: () => void;
loadChildNodesHandler: ( loadChildNodesHandler: (
node: SourceType, node: SourceType,
direction: EdgeTypeEnum direction: EdgeTypeEnum

View File

@ -59,7 +59,6 @@ import {
import { SourceType } from '../../components/SearchedData/SearchedData.interface'; import { SourceType } from '../../components/SearchedData/SearchedData.interface';
import { import {
ELEMENT_DELETE_STATE, ELEMENT_DELETE_STATE,
ZOOM_TRANSITION_DURATION,
ZOOM_VALUE, ZOOM_VALUE,
} from '../../constants/Lineage.constants'; } from '../../constants/Lineage.constants';
import { mockDatasetData } from '../../constants/mockTourData.constants'; import { mockDatasetData } from '../../constants/mockTourData.constants';
@ -78,6 +77,7 @@ import { useFqn } from '../../hooks/useFqn';
import { getLineageDataByFQN, updateLineageEdge } from '../../rest/lineageAPI'; import { getLineageDataByFQN, updateLineageEdge } from '../../rest/lineageAPI';
import { import {
addLineageHandler, addLineageHandler,
centerNodePosition,
createEdges, createEdges,
createNewEdge, createNewEdge,
createNodes, createNodes,
@ -124,14 +124,10 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const [selectedNode, setSelectedNode] = useState<SourceType>( const [selectedNode, setSelectedNode] = useState<SourceType>(
{} as SourceType {} as SourceType
); );
const [activeLayer, setActiveLayer] = useState<LineageLayerView[]>([ const [activeLayer, setActiveLayer] = useState<LineageLayerView[]>([]);
LineageLayerView.COLUMN,
]);
const [activeNode, setActiveNode] = useState<Node>(); const [activeNode, setActiveNode] = useState<Node>();
const [selectedColumn, setSelectedColumn] = useState<string>(''); const [selectedColumn, setSelectedColumn] = useState<string>('');
const [showAddEdgeModal, setShowAddEdgeModal] = useState<boolean>(false); const [showAddEdgeModal, setShowAddEdgeModal] = useState<boolean>(false);
const [expandedNodes, setExpandedNodes] = useState<string[]>([]);
const [expandAllColumns, setExpandAllColumns] = useState(false);
const [selectedEdge, setSelectedEdge] = useState<Edge>(); const [selectedEdge, setSelectedEdge] = useState<Edge>();
const [entityLineage, setEntityLineage] = useState<EntityLineageResponse>({ const [entityLineage, setEntityLineage] = useState<EntityLineageResponse>({
nodes: [], nodes: [],
@ -679,10 +675,6 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
}, []); }, []);
const onInitReactFlow = (reactFlowInstance: ReactFlowInstance) => { const onInitReactFlow = (reactFlowInstance: ReactFlowInstance) => {
setTimeout(() => {
onLoad(reactFlowInstance);
}, 0);
setReactFlowInstance(reactFlowInstance); setReactFlowInstance(reactFlowInstance);
}; };
@ -698,43 +690,6 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
setZoomValue(value); setZoomValue(value);
}, []); }, []);
const toggleColumnView = useCallback(() => {
const updatedVal = !expandAllColumns;
setExpandAllColumns(updatedVal);
setNodes((prevNodes) => {
const updatedNode = prevNodes.map((node) => {
const nodeId = node.data.node.id;
// Update the expandedNodes state based on the toggle value
if (updatedVal && !expandedNodes.includes(nodeId)) {
setExpandedNodes((prevExpandedNodes) => [
...prevExpandedNodes,
nodeId,
]);
} else if (!updatedVal) {
setExpandedNodes((prevExpandedNodes) =>
prevExpandedNodes.filter((id) => id !== nodeId)
);
}
return node;
});
const { edge, node } = getLayoutedElements(
{
node: updatedNode,
edge: edges,
},
EntityLineageDirection.LEFT_RIGHT,
updatedVal
);
setEdges(edge);
return node;
});
}, [expandAllColumns, expandedNodes, edges]);
const onRemove = useCallback(async () => { const onRemove = useCallback(async () => {
try { try {
setDeletionState({ ...ELEMENT_DELETE_STATE, loading: true }); setDeletionState({ ...ELEMENT_DELETE_STATE, loading: true });
@ -1066,14 +1021,42 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
); );
const selectNode = (node: Node) => { const selectNode = (node: Node) => {
const { position } = node; centerNodePosition(node, reactFlowInstance);
// moving selected node in center
reactFlowInstance?.setCenter(position.x, position.y, {
duration: ZOOM_TRANSITION_DURATION,
zoom: zoomValue,
});
}; };
const repositionLayout = useCallback(
(activateNode = false) => {
const isColView = activeLayer.includes(LineageLayerView.COLUMN);
const { node, edge } = getLayoutedElements(
{
node: nodes,
edge: edges,
},
EntityLineageDirection.LEFT_RIGHT,
isColView
);
setNodes(node);
setEdges(edge);
const rootNode = node.find((n) => n.data.isRootNode);
if (!rootNode) {
if (activateNode && reactFlowInstance) {
onLoad(reactFlowInstance); // Call fitview in case of pipeline
}
return;
}
// Center the root node in the view
centerNodePosition(rootNode, reactFlowInstance);
if (activateNode) {
onNodeClick(rootNode);
}
},
[reactFlowInstance, activeLayer, nodes, edges, onNodeClick]
);
const redrawLineage = useCallback( const redrawLineage = useCallback(
(lineageData: EntityLineageResponse) => { (lineageData: EntityLineageResponse) => {
const allNodes = uniqWith( const allNodes = uniqWith(
@ -1087,7 +1070,8 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const updatedNodes = createNodes( const updatedNodes = createNodes(
allNodes, allNodes,
lineageData.edges ?? [], lineageData.edges ?? [],
decodedFqn decodedFqn,
activeLayer.includes(LineageLayerView.COLUMN)
); );
const updatedEdges = createEdges( const updatedEdges = createEdges(
allNodes, allNodes,
@ -1105,11 +1089,11 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
); );
setUpstreamDownstreamData(data); setUpstreamDownstreamData(data);
if (activeNode) { if (activeNode && !isEditMode) {
selectNode(activeNode); selectNode(activeNode);
} }
}, },
[decodedFqn, activeNode] [decodedFqn, activeNode, activeLayer, isEditMode]
); );
useEffect(() => { useEffect(() => {
@ -1164,19 +1148,15 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
}, [isEditMode, deletePressed, backspacePressed, activeNode, selectedEdge]); }, [isEditMode, deletePressed, backspacePressed, activeNode, selectedEdge]);
useEffect(() => { useEffect(() => {
const { node, edge } = getLayoutedElements( repositionLayout();
{
node: nodes,
edge: edges,
},
EntityLineageDirection.LEFT_RIGHT,
activeLayer.includes(LineageLayerView.COLUMN)
);
setNodes(node);
setEdges(edge);
}, [activeLayer]); }, [activeLayer]);
useEffect(() => {
if (reactFlowInstance?.viewportInitialized) {
repositionLayout(true); // Activate the root node
}
}, [reactFlowInstance?.viewportInitialized]);
const activityFeedContextValues = useMemo(() => { const activityFeedContextValues = useMemo(() => {
return { return {
isDrawerOpen, isDrawerOpen,
@ -1191,10 +1171,8 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
selectedColumn, selectedColumn,
zoomValue, zoomValue,
status, status,
expandedNodes,
tracedNodes, tracedNodes,
tracedColumns, tracedColumns,
expandAllColumns,
upstreamDownstreamData, upstreamDownstreamData,
init, init,
activeLayer, activeLayer,
@ -1210,7 +1188,6 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
onZoomUpdate, onZoomUpdate,
updateEntityType, updateEntityType,
onDrawerClose, onDrawerClose,
toggleColumnView,
loadChildNodesHandler, loadChildNodesHandler,
fetchLineageData, fetchLineageData,
removeNodeHandler, removeNodeHandler,
@ -1236,10 +1213,8 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
selectedColumn, selectedColumn,
zoomValue, zoomValue,
status, status,
expandedNodes,
tracedNodes, tracedNodes,
tracedColumns, tracedColumns,
expandAllColumns,
upstreamDownstreamData, upstreamDownstreamData,
init, init,
activeLayer, activeLayer,
@ -1257,7 +1232,6 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
updateEntityType, updateEntityType,
loadChildNodesHandler, loadChildNodesHandler,
fetchLineageData, fetchLineageData,
toggleColumnView,
removeNodeHandler, removeNodeHandler,
onNodeClick, onNodeClick,
onEdgeClick, onEdgeClick,

View File

@ -118,6 +118,7 @@
@welcome-page-height: calc(100vh - 112px); @welcome-page-height: calc(100vh - 112px);
@data-product-page-height: calc(100vh - 156px); @data-product-page-height: calc(100vh - 156px);
@glossary-page-tab-height: calc(100vh - 206px); @glossary-page-tab-height: calc(100vh - 206px);
@lineage-sidebar-width: 110px;
// Severity // Severity
@severity-1: #9c0700; @severity-1: #9c0700;

View File

@ -68,6 +68,7 @@ import {
LINEAGE_EXPORT_HEADERS, LINEAGE_EXPORT_HEADERS,
NODE_HEIGHT, NODE_HEIGHT,
NODE_WIDTH, NODE_WIDTH,
ZOOM_TRANSITION_DURATION,
ZOOM_VALUE, ZOOM_VALUE,
} from '../constants/Lineage.constants'; } from '../constants/Lineage.constants';
import { import {
@ -120,6 +121,22 @@ export const onLoad = (reactFlowInstance: ReactFlowInstance) => {
reactFlowInstance.fitView(); reactFlowInstance.fitView();
reactFlowInstance.zoomTo(ZOOM_VALUE); reactFlowInstance.zoomTo(ZOOM_VALUE);
}; };
export const centerNodePosition = (
node: Node,
reactFlowInstance?: ReactFlowInstance
) => {
const { position, width } = node;
reactFlowInstance?.setCenter(
position.x + (width ?? 1 / 2),
position.y + NODE_HEIGHT / 2,
{
zoom: ZOOM_VALUE,
duration: ZOOM_TRANSITION_DURATION,
}
);
};
/* eslint-disable-next-line */ /* eslint-disable-next-line */
export const onNodeMouseEnter = (_event: ReactMouseEvent, _node: Node) => { export const onNodeMouseEnter = (_event: ReactMouseEvent, _node: Node) => {
return; return;
@ -631,7 +648,8 @@ const getNodeType = (
export const createNodes = ( export const createNodes = (
nodesData: EntityReference[], nodesData: EntityReference[],
edgesData: EdgeDetails[], edgesData: EdgeDetails[],
entityFqn: string entityFqn: string,
isExpanded = false
) => { ) => {
const uniqueNodesData = removeDuplicateNodes(nodesData).sort((a, b) => const uniqueNodesData = removeDuplicateNodes(nodesData).sort((a, b) =>
getEntityName(a).localeCompare(getEntityName(b)) getEntityName(a).localeCompare(getEntityName(b))
@ -651,7 +669,7 @@ export const createNodes = (
// Add nodes to the graph // Add nodes to the graph
uniqueNodesData.forEach((node) => { uniqueNodesData.forEach((node) => {
const { childrenHeight } = getEntityChildrenAndLabel(node as SourceType); const { childrenHeight } = getEntityChildrenAndLabel(node as SourceType);
const nodeHeight = childrenHeight + 220; const nodeHeight = isExpanded ? childrenHeight + 220 : NODE_HEIGHT;
graph.setNode(node.id, { width: NODE_WIDTH, height: nodeHeight }); graph.setNode(node.id, { width: NODE_WIDTH, height: nodeHeight });
}); });