@@ -28,14 +28,17 @@ import {
2828 createGraphLayout ,
2929 toNodeOrEdgeId ,
3030 type GraphNodeData ,
31- mergeLineage ,
31+ mergeLineageWithColumns ,
32+ hasNoModels ,
33+ mergeConnections ,
3234} from './help'
3335import {
3436 debounceAsync ,
3537 debounceSync ,
3638 isArrayEmpty ,
3739 isArrayNotEmpty ,
3840 isFalse ,
41+ isNil ,
3942 isTrue ,
4043} from '../../../utils'
4144import { EnumSize , EnumVariant } from '~/types/enum'
@@ -45,6 +48,7 @@ import {
4548 type Column ,
4649 columnLineageApiLineageModelNameColumnNameGet ,
4750 type ColumnLineageApiLineageModelNameColumnNameGet200 ,
51+ type LineageColumn ,
4852} from '@api/client'
4953import Loading from '@components/loading/Loading'
5054import Spinner from '@components/logo/Spinner'
@@ -57,7 +61,6 @@ import { useLineageFlow } from './context'
5761import { useQueryClient } from '@tanstack/react-query'
5862import Input from '@components/input/Input'
5963import { type ResponseWithDetail } from '@api/instance'
60- import { Divider } from '@components/divider/Divider'
6164
6265const ModelColumnDisplay = memo ( function ModelColumnDisplay ( {
6366 columnName,
@@ -108,7 +111,10 @@ const ModelNodeHandles = memo(function ModelNodeHandles({
108111 const updateNodeInternals = useUpdateNodeInternals ( )
109112
110113 useEffect ( ( ) => {
111- updateNodeInternals ( nodeId )
114+ // TODO: This is a hack to fix the issue where the handles are not rendered yet
115+ setTimeout ( ( ) => {
116+ updateNodeInternals ( nodeId )
117+ } , 100 )
112118 } , [ hasLeft , hasRight ] )
113119
114120 return (
@@ -140,7 +146,7 @@ const ModelNodeHandles = memo(function ModelNodeHandles({
140146 className = { clsx (
141147 'w-2 h-2 rounded-full !bg-secondary-500 dark:!bg-primary-500' ,
142148 ) }
143- > </ Handle >
149+ / >
144150 ) }
145151 </ div >
146152 )
@@ -291,27 +297,7 @@ const ModelColumn = memo(function ModelColumn({
291297
292298 debouncedGetColumnLineage ( column . name )
293299 . then ( data => {
294- const models =
295- data as ColumnLineageApiLineageModelNameColumnNameGet200
296-
297- setIsEmpty ( ( ) => {
298- for ( const modelName in models ) {
299- const model = models [ modelName ]
300-
301- if ( model == null ) continue
302-
303- for ( const columnName in model ) {
304- const lineage = model [ columnName ]
305-
306- if ( lineage == null ) continue
307-
308- return Object . keys ( lineage . models ?? { } ) . length === 0
309- }
310- }
311-
312- return false
313- } )
314-
300+ setIsEmpty ( hasNoModels ( ) )
315301 updateColumnLineage ( data )
316302 } )
317303 . catch ( error => {
@@ -397,7 +383,6 @@ const ModelColumns = memo(function ModelColumns({
397383 const queryClient = useQueryClient ( )
398384
399385 const {
400- models,
401386 connections,
402387 isActiveColumn,
403388 setConnections,
@@ -456,75 +441,16 @@ const ModelColumns = memo(function ModelColumns({
456441
457442 const updateColumnLineage = useCallback (
458443 function updateColumnLineage (
459- columnLineage : ColumnLineageApiLineageModelNameColumnNameGet200 ,
444+ columnLineage : Record < string , Record < string , LineageColumn > > = { } ,
460445 ) : void {
461- setLineage ( lineage => mergeLineage ( models , lineage , columnLineage ) )
462- setConnections ( connections => {
463- for ( const modelName in columnLineage ) {
464- const model = columnLineage [ modelName ]
465-
466- if ( model == null ) continue
467-
468- for ( const columnName in model ) {
469- const column = model [ columnName ]
470-
471- if ( column ?. models == null ) continue
472-
473- const connectionSource = connections . get (
474- toNodeOrEdgeId ( modelName , columnName ) ,
475- ) ?? {
476- left : [ ] ,
477- right : [ ] ,
478- }
479-
480- Object . entries ( column . models ) . forEach ( ( [ id , columns ] ) => {
481- columns . forEach ( column => {
482- const connectionTarget = connections . get (
483- toNodeOrEdgeId ( id , column ) ,
484- ) ?? {
485- left : [ ] ,
486- right : [ ] ,
487- }
488-
489- connectionTarget . right = Array . from (
490- new Set (
491- connectionTarget . right . concat (
492- toNodeOrEdgeId ( modelName , columnName ) ,
493- ) ,
494- ) ,
495- )
496- connectionSource . left = Array . from (
497- new Set (
498- connectionSource . left . concat ( toNodeOrEdgeId ( id , column ) ) ,
499- ) ,
500- )
501-
502- connections . set ( toNodeOrEdgeId ( id , column ) , connectionTarget )
503- connections . set (
504- toNodeOrEdgeId ( modelName , columnName ) ,
505- connectionSource ,
506- )
507- } )
508- } )
509-
510- const modelColumnConnectionsLeft = (
511- connections . get ( toNodeOrEdgeId ( modelName , columnName ) ) ?. left ?? [ ]
512- ) . map ( id => toNodeOrEdgeId ( 'right' , id ) )
513- const modelColumnConnectionsRight = (
514- connections . get ( toNodeOrEdgeId ( modelName , columnName ) ) ?. right ??
515- [ ]
516- ) . map ( id => toNodeOrEdgeId ( 'left' , id ) )
517-
518- addActiveEdges (
519- modelColumnConnectionsLeft . concat ( modelColumnConnectionsRight ) ,
520- )
521- }
522- }
523-
524- return new Map ( connections )
525- } )
446+ setLineage ( lineage =>
447+ mergeLineageWithColumns ( structuredClone ( lineage ) , columnLineage ) ,
448+ )
449+ setConnections ( connections =>
450+ mergeConnections ( columnLineage , connections , addActiveEdges ) ,
451+ )
526452 } ,
527- [ models , addActiveEdges , setConnections ] ,
453+ [ addActiveEdges , setConnections ] ,
528454 )
529455
530456 const isSelectManually = useCallback (
@@ -644,7 +570,6 @@ const ModelColumns = memo(function ModelColumns({
644570 />
645571 ) ) }
646572 </ div >
647- < Divider className = "border-primary-500" />
648573 { columns . length > limit && (
649574 < div className = "py-2 flex justify-center bg-theme-lighter" >
650575 < Button
@@ -710,27 +635,23 @@ function ModelColumnLineage({
710635 'border-4 border-brand-500' : [ model . name ] ,
711636 }
712637
713- void load ( )
714-
715- async function load ( ) : Promise < void > {
716- const nodesAndEdges = getNodesAndEdges ( {
717- lineage,
718- highlightedNodes : highlightedNodes ?? highlightedNodesDefault ,
719- models,
720- nodes,
721- edges,
722- model,
723- withColumns,
724- } )
638+ const nodesAndEdges = getNodesAndEdges ( {
639+ lineage,
640+ highlightedNodes : highlightedNodes ?? highlightedNodesDefault ,
641+ models,
642+ nodes,
643+ edges,
644+ model,
645+ withColumns,
646+ } )
725647
726- void createGraphLayout ( nodesAndEdges ) . then ( layout => {
727- setNodes ( layout . nodes )
728- setEdges ( toggleEdge ( layout . edges ) )
729- setIsBuildingLayout (
730- isArrayEmpty ( layout . nodes ) || isArrayEmpty ( layout . edges ) ,
731- )
732- } )
733- }
648+ void createGraphLayout ( nodesAndEdges ) . then ( layout => {
649+ setNodes ( layout . nodes )
650+ setEdges ( toggleEdge ( layout . edges ) )
651+ setIsBuildingLayout (
652+ isArrayEmpty ( layout . nodes ) || isArrayEmpty ( layout . edges ) ,
653+ )
654+ } )
734655 } , [ model . name , models , highlightedNodes , lineage ] )
735656
736657 useEffect ( ( ) => {
@@ -777,16 +698,30 @@ function ModelNode({
777698 sourcePosition,
778699 targetPosition,
779700} : NodeProps & { data : GraphNodeData } ) : JSX . Element {
780- const { models, withColumns, handleClickModel } = useLineageFlow ( )
701+ const {
702+ models,
703+ withColumns,
704+ handleClickModel,
705+ lineage = { } ,
706+ } = useLineageFlow ( )
781707
782708 const { model, columns } = useMemo ( ( ) => {
783709 const model = models . get ( id )
710+ const columns = model ?. columns ?? [ ]
711+
712+ Object . keys ( lineage [ id ] ?. columns ?? { } ) . forEach ( ( column : string ) => {
713+ const found = columns . find ( ( { name } ) => name === column )
714+
715+ if ( isNil ( found ) ) {
716+ columns . push ( { name : column , type : 'UNKNOWN' } )
717+ }
718+ } )
784719
785720 return {
786721 model,
787- columns : model ?. columns ?? [ ] ,
722+ columns,
788723 }
789- } , [ id , models ] )
724+ } , [ id , models , lineage ] )
790725
791726 const handleClick = useCallback (
792727 ( e : MouseEvent ) => {
@@ -802,12 +737,15 @@ function ModelNode({
802737 )
803738 const splat = data . highlightedNodes ?. [ '*' ]
804739 const isInteractive = isTrue ( data . isInteractive ) && handleClickModel != null
740+ const isTable = data . type === 'table'
741+ const showColumns = withColumns && isArrayNotEmpty ( columns )
805742
806743 return (
807744 < div
808745 className = { clsx (
809- 'text-xs font-semibold text-secondary-500 dark:text-primary-100 rounded-xl shadow-lg relative z-1' ,
746+ 'text-xs font-semibold rounded-xl shadow-lg relative z-1' ,
810747 highlighted == null ? splat : highlighted ,
748+ isTable ? '' : 'text-secondary-500 dark:text-primary-100' ,
811749 ) }
812750 >
813751 < div className = "drag-handle" >
@@ -816,24 +754,32 @@ function ModelNode({
816754 type = { model ?. type }
817755 label = { data . label }
818756 className = { clsx (
819- 'bg-secondary-100 dark:bg-primary-900 py-2' ,
820- withColumns ? 'rounded-t-lg' : 'rounded-lg' ,
757+ 'py-2' ,
758+ showColumns ? 'rounded-t-lg' : 'rounded-lg' ,
759+ isTable ? 'bg-neutral-600' : 'bg-secondary-100 dark:bg-primary-900' ,
821760 ) }
822761 hasLeft = { sourcePosition === Position . Left }
823762 hasRight = { targetPosition === Position . Right }
824763 handleClick = { isInteractive ? handleClick : undefined }
825764 />
826765 </ div >
827- { withColumns && isArrayNotEmpty ( columns ) && (
766+ { showColumns && isArrayNotEmpty ( columns ) && (
828767 < >
829768 < ModelColumns
830769 className = "max-h-[15rem]"
831770 nodeId = { id }
832771 columns = { columns }
833- disabled = { model ?. type === 'python' }
772+ disabled = { model ?. type === 'python' || data . type !== 'model' }
834773 withHandles = { true }
835774 />
836- < div className = "rounded-b-lg bg-secondary-100 dark:bg-primary-900 py-1" > </ div >
775+ < div
776+ className = { clsx (
777+ 'rounded-b-lg py-1' ,
778+ isTable
779+ ? 'bg-neutral-600'
780+ : 'bg-secondary-100 dark:bg-primary-900' ,
781+ ) }
782+ > </ div >
837783 </ >
838784 ) }
839785 </ div >
0 commit comments