mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 10:39:30 +00:00 
			
		
		
		
	ui: display source of lineage (#15391)
* ui: display lineage source * added locale files * rename dbt source name * test: added unit test case
This commit is contained in:
		
							parent
							
								
									7424b2f430
								
							
						
					
					
						commit
						ec475bce03
					
				| @ -21,8 +21,10 @@ import { Node } from 'reactflow'; | |||||||
| import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; | import { ReactComponent as EditIcon } from '../../../assets/svg/edit-new.svg'; | ||||||
| import DescriptionV1 from '../../../components/common/EntityDescription/DescriptionV1'; | import DescriptionV1 from '../../../components/common/EntityDescription/DescriptionV1'; | ||||||
| import { DE_ACTIVE_COLOR } from '../../../constants/constants'; | import { DE_ACTIVE_COLOR } from '../../../constants/constants'; | ||||||
|  | import { LINEAGE_SOURCE } from '../../../constants/Lineage.constants'; | ||||||
| import { CSMode } from '../../../enums/codemirror.enum'; | import { CSMode } from '../../../enums/codemirror.enum'; | ||||||
| import { EntityType } from '../../../enums/entity.enum'; | import { EntityType } from '../../../enums/entity.enum'; | ||||||
|  | import { Source } from '../../../generated/type/entityLineage'; | ||||||
| import { getNameFromFQN } from '../../../utils/CommonUtils'; | import { getNameFromFQN } from '../../../utils/CommonUtils'; | ||||||
| import { getLineageDetailsObject } from '../../../utils/EntityLineageUtils'; | import { getLineageDetailsObject } from '../../../utils/EntityLineageUtils'; | ||||||
| import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; | import entityUtilClassBase from '../../../utils/EntityUtilClassBase'; | ||||||
| @ -266,6 +268,15 @@ const EdgeInfoDrawer = ({ | |||||||
|                 </Typography.Paragraph> |                 </Typography.Paragraph> | ||||||
|               )} |               )} | ||||||
|             </Col> |             </Col> | ||||||
|  |             <Col> | ||||||
|  |               <Divider /> | ||||||
|  |               <Typography.Paragraph className="right-panel-label m-b-sm"> | ||||||
|  |                 {`${t('label.lineage-source')}`} | ||||||
|  |               </Typography.Paragraph> | ||||||
|  |               <Typography.Text className="m-b-0"> | ||||||
|  |                 {LINEAGE_SOURCE[edgeEntity.source as keyof typeof Source]} | ||||||
|  |               </Typography.Text> | ||||||
|  |             </Col> | ||||||
|           </Row> |           </Row> | ||||||
|         )} |         )} | ||||||
|       </Drawer> |       </Drawer> | ||||||
|  | |||||||
| @ -0,0 +1,163 @@ | |||||||
|  | /* | ||||||
|  |  *  Copyright 2024 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 { act, fireEvent, render, screen } from '@testing-library/react'; | ||||||
|  | import React from 'react'; | ||||||
|  | import { Edge } from 'reactflow'; | ||||||
|  | import { MOCK_NODES_AND_EDGES } from '../../../mocks/Lineage.mock'; | ||||||
|  | import EdgeInfoDrawer from './EdgeInfoDrawer.component'; | ||||||
|  | 
 | ||||||
|  | jest.mock('../../../components/common/EntityDescription/DescriptionV1', () => | ||||||
|  |   jest.fn().mockImplementation(({ onDescriptionUpdate }) => ( | ||||||
|  |     <div data-testid="description-v1"> | ||||||
|  |       DescriptionV1 | ||||||
|  |       <button | ||||||
|  |         data-testid="update-description-button" | ||||||
|  |         onClick={() => onDescriptionUpdate('updatedHTML')}> | ||||||
|  |         Update Description | ||||||
|  |       </button> | ||||||
|  |     </div> | ||||||
|  |   )) | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | jest.mock('../../../utils/CommonUtils', () => ({ | ||||||
|  |   getNameFromFQN: jest.fn().mockReturnValue('getNameFromFQN'), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | jest.mock('../../../utils/EntityUtils', () => ({ | ||||||
|  |   getEntityName: jest.fn().mockReturnValue('username'), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | jest.mock('../../common/Loader/Loader', () => | ||||||
|  |   jest.fn().mockImplementation(() => <div>Loader</div>) | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | jest.mock('../../Database/SchemaEditor/SchemaEditor', () => { | ||||||
|  |   return jest.fn().mockImplementation(() => <div>SchemaEditor.component</div>); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | jest.mock('../../Modals/ModalWithQueryEditor/ModalWithQueryEditor', () => { | ||||||
|  |   return jest.fn().mockImplementation(() => <div>ModalWithQueryEditor</div>); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | jest.mock('antd', () => ({ | ||||||
|  |   ...jest.requireActual('antd'), | ||||||
|  |   Drawer: jest.fn().mockImplementation(({ children, title }) => ( | ||||||
|  |     <div data-testid="drawer-component"> | ||||||
|  |       Drawer | ||||||
|  |       <div data-testid="title">{title}</div> | ||||||
|  |       <div>{children}</div> | ||||||
|  |     </div> | ||||||
|  |   )), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | const mockOnEdgeDetailsUpdate = jest.fn(); | ||||||
|  | const mockEdgeInfoDrawer = { | ||||||
|  |   edge: { | ||||||
|  |     id: 'edge-5c97531f-d164-4707-842e-af52e0c43e26-5d816d56-40a2-493f-ae9d-012f1cd337dd', | ||||||
|  |     source: '5c97531f-d164-4707-842e-af52e0c43e26', | ||||||
|  |     target: '5d816d56-40a2-493f-ae9d-012f1cd337dd', | ||||||
|  |     type: 'buttonedge', | ||||||
|  |     animated: false, | ||||||
|  |     style: { | ||||||
|  |       strokeWidth: '2px', | ||||||
|  |     }, | ||||||
|  |     markerEnd: { | ||||||
|  |       type: 'arrowclosed', | ||||||
|  |     }, | ||||||
|  |     data: { | ||||||
|  |       edge: { | ||||||
|  |         toEntity: { | ||||||
|  |           fqn: 'RedshiftProd.dev.demo_dbt_jaffle.customers', | ||||||
|  |           id: '5d816d56-40a2-493f-ae9d-012f1cd337dd', | ||||||
|  |           type: 'table', | ||||||
|  |         }, | ||||||
|  |         pipeline: null, | ||||||
|  |         fromEntity: { | ||||||
|  |           fqn: 'RedshiftProd.dev.demo_dbt_jaffle.stg_orders', | ||||||
|  |           id: '5c97531f-d164-4707-842e-af52e0c43e26', | ||||||
|  |           type: 'table', | ||||||
|  |         }, | ||||||
|  |         sqlQuery: null, | ||||||
|  |         description: null, | ||||||
|  |         source: 'DbtLineage', | ||||||
|  |         doc_id: | ||||||
|  |           '5c97531f-d164-4707-842e-af52e0c43e26-5d816d56-40a2-493f-ae9d-012f1cd337dd', | ||||||
|  |       }, | ||||||
|  |       isColumnLineage: false, | ||||||
|  |       isPipelineRootNode: false, | ||||||
|  |     }, | ||||||
|  |     selected: true, | ||||||
|  |   } as Edge, | ||||||
|  |   nodes: MOCK_NODES_AND_EDGES.nodes, | ||||||
|  |   visible: true, | ||||||
|  |   hasEditAccess: true, | ||||||
|  |   onEdgeDetailsUpdate: mockOnEdgeDetailsUpdate, | ||||||
|  |   onClose: jest.fn(), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | describe('EdgeInfoDrawer Component', () => { | ||||||
|  |   it('should render the component', async () => { | ||||||
|  |     render(<EdgeInfoDrawer {...mockEdgeInfoDrawer} />); | ||||||
|  | 
 | ||||||
|  |     expect(await screen.findByTestId('title')).toHaveTextContent( | ||||||
|  |       'label.edge-information' | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     expect(await screen.findByTestId('description-v1')).toBeInTheDocument(); | ||||||
|  |     expect( | ||||||
|  |       await screen.findByText('label.sql-uppercase-query') | ||||||
|  |     ).toBeInTheDocument(); | ||||||
|  |     expect(await screen.findByTestId('edit-sql')).toBeInTheDocument(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should render no query if no query is present', async () => { | ||||||
|  |     render(<EdgeInfoDrawer {...mockEdgeInfoDrawer} />); | ||||||
|  | 
 | ||||||
|  |     expect( | ||||||
|  |       await screen.findByText('server.no-query-available') | ||||||
|  |     ).toBeInTheDocument(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should render source of lineage', async () => { | ||||||
|  |     render(<EdgeInfoDrawer {...mockEdgeInfoDrawer} />); | ||||||
|  | 
 | ||||||
|  |     expect(await screen.findByText('label.lineage-source')).toBeInTheDocument(); | ||||||
|  |     expect(await screen.findByText('dbt Lineage')).toBeInTheDocument(); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should call onEdgeDetailsUpdate on update description', async () => { | ||||||
|  |     render(<EdgeInfoDrawer {...mockEdgeInfoDrawer} />); | ||||||
|  |     const updateDescriptionButton = await screen.findByTestId( | ||||||
|  |       'update-description-button' | ||||||
|  |     ); | ||||||
|  |     await act(async () => { | ||||||
|  |       fireEvent.click(updateDescriptionButton); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     expect(mockOnEdgeDetailsUpdate).toHaveBeenCalledWith( | ||||||
|  |       expect.objectContaining({ | ||||||
|  |         edge: expect.objectContaining({ | ||||||
|  |           lineageDetails: expect.objectContaining({ | ||||||
|  |             description: 'updatedHTML', | ||||||
|  |           }), | ||||||
|  |         }), | ||||||
|  |       }) | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   it('should not render edit SQL button if has no edit access', () => { | ||||||
|  |     render(<EdgeInfoDrawer {...mockEdgeInfoDrawer} hasEditAccess={false} />); | ||||||
|  | 
 | ||||||
|  |     expect(screen.queryByTestId('edit-sql')).not.toBeInTheDocument(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
| @ -14,6 +14,7 @@ | |||||||
| import { t } from 'i18next'; | import { t } from 'i18next'; | ||||||
| import { ElementLoadingState } from '../components/Entity/EntityLineage/EntityLineage.interface'; | import { ElementLoadingState } from '../components/Entity/EntityLineage/EntityLineage.interface'; | ||||||
| import { SearchIndex } from '../enums/search.enum'; | import { SearchIndex } from '../enums/search.enum'; | ||||||
|  | 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.75; | ||||||
| @ -73,3 +74,13 @@ export const LINEAGE_DEFAULT_QUICK_FILTERS = [ | |||||||
|   'owner.displayName.keyword', |   'owner.displayName.keyword', | ||||||
|   'tags.tagFQN', |   'tags.tagFQN', | ||||||
| ]; | ]; | ||||||
|  | 
 | ||||||
|  | export const LINEAGE_SOURCE: { [key in Source]: string } = { | ||||||
|  |   [Source.DashboardLineage]: 'Dashboard Lineage', | ||||||
|  |   [Source.DbtLineage]: 'dbt Lineage', | ||||||
|  |   [Source.Manual]: 'Manual', | ||||||
|  |   [Source.PipelineLineage]: 'Pipeline Lineage', | ||||||
|  |   [Source.QueryLineage]: 'Query Lineage', | ||||||
|  |   [Source.SparkLineage]: 'Spark Lineage', | ||||||
|  |   [Source.ViewLineage]: 'View Lineage', | ||||||
|  | }; | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "Abstammungsinjektion", |     "lineage-ingestion": "Abstammungsinjektion", | ||||||
|     "lineage-lowercase": "Abstammung", |     "lineage-lowercase": "Abstammung", | ||||||
|     "lineage-node-lowercase": "Abstammungsknoten", |     "lineage-node-lowercase": "Abstammungsknoten", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "Liste", |     "list": "Liste", | ||||||
|     "list-entity": "{{entity}}-Liste", |     "list-entity": "{{entity}}-Liste", | ||||||
|     "live": "Live", |     "live": "Live", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "Lineage Ingestion", |     "lineage-ingestion": "Lineage Ingestion", | ||||||
|     "lineage-lowercase": "lineage", |     "lineage-lowercase": "lineage", | ||||||
|     "lineage-node-lowercase": "lineage node", |     "lineage-node-lowercase": "lineage node", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "List", |     "list": "List", | ||||||
|     "list-entity": "List {{entity}}", |     "list-entity": "List {{entity}}", | ||||||
|     "live": "Live", |     "live": "Live", | ||||||
|  | |||||||
| @ -613,9 +613,10 @@ | |||||||
|     "lineage": "Linaje", |     "lineage": "Linaje", | ||||||
|     "lineage-config": "Configuración del linaje", |     "lineage-config": "Configuración del linaje", | ||||||
|     "lineage-data-lowercase": "lineage data", |     "lineage-data-lowercase": "lineage data", | ||||||
|     "lineage-ingestion": "Ingesta de linaje", |     "lineage-ingestion": "Ingesta de lineaje", | ||||||
|     "lineage-lowercase": "linaje", |     "lineage-lowercase": "lineaje", | ||||||
|     "lineage-node-lowercase": "nodo de linaje", |     "lineage-node-lowercase": "lineage node", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "Lista", |     "list": "Lista", | ||||||
|     "list-entity": "Lista de {{entity}}", |     "list-entity": "Lista de {{entity}}", | ||||||
|     "live": "Vivo", |     "live": "Vivo", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "Ingestion de la Traçabilité", |     "lineage-ingestion": "Ingestion de la Traçabilité", | ||||||
|     "lineage-lowercase": "traçabilité", |     "lineage-lowercase": "traçabilité", | ||||||
|     "lineage-node-lowercase": "Nœud de Traçabilité", |     "lineage-node-lowercase": "Nœud de Traçabilité", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "Liste", |     "list": "Liste", | ||||||
|     "list-entity": "Liste de {{entity}}", |     "list-entity": "Liste de {{entity}}", | ||||||
|     "live": "En Direct", |     "live": "En Direct", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "כניסת שורשים", |     "lineage-ingestion": "כניסת שורשים", | ||||||
|     "lineage-lowercase": "שורשים", |     "lineage-lowercase": "שורשים", | ||||||
|     "lineage-node-lowercase": "צומת שורש", |     "lineage-node-lowercase": "צומת שורש", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "רשימה", |     "list": "רשימה", | ||||||
|     "list-entity": "רשימת {{entity}}", |     "list-entity": "רשימת {{entity}}", | ||||||
|     "live": "שידור חי", |     "live": "שידור חי", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "リネージのインジェスチョン", |     "lineage-ingestion": "リネージのインジェスチョン", | ||||||
|     "lineage-lowercase": "リネージ", |     "lineage-lowercase": "リネージ", | ||||||
|     "lineage-node-lowercase": "lineage node", |     "lineage-node-lowercase": "lineage node", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "リスト", |     "list": "リスト", | ||||||
|     "list-entity": "{{entity}}のリスト", |     "list-entity": "{{entity}}のリスト", | ||||||
|     "live": "Live", |     "live": "Live", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "Herkomstingestie", |     "lineage-ingestion": "Herkomstingestie", | ||||||
|     "lineage-lowercase": "herkomst", |     "lineage-lowercase": "herkomst", | ||||||
|     "lineage-node-lowercase": "herkomstknooppunt", |     "lineage-node-lowercase": "herkomstknooppunt", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "Lijst", |     "list": "Lijst", | ||||||
|     "list-entity": "Lijst {{entity}}", |     "list-entity": "Lijst {{entity}}", | ||||||
|     "live": "Live", |     "live": "Live", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "Ingestão de Linhagem", |     "lineage-ingestion": "Ingestão de Linhagem", | ||||||
|     "lineage-lowercase": "linhagem", |     "lineage-lowercase": "linhagem", | ||||||
|     "lineage-node-lowercase": "nó de linhagem", |     "lineage-node-lowercase": "nó de linhagem", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "Lista", |     "list": "Lista", | ||||||
|     "list-entity": "Listar {{entity}}", |     "list-entity": "Listar {{entity}}", | ||||||
|     "live": "Ao Vivo", |     "live": "Ao Vivo", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "Получение проихождения", |     "lineage-ingestion": "Получение проихождения", | ||||||
|     "lineage-lowercase": "происходение", |     "lineage-lowercase": "происходение", | ||||||
|     "lineage-node-lowercase": "узел происхождения", |     "lineage-node-lowercase": "узел происхождения", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "Список", |     "list": "Список", | ||||||
|     "list-entity": "Список {{entity}}", |     "list-entity": "Список {{entity}}", | ||||||
|     "live": "Процесс", |     "live": "Процесс", | ||||||
|  | |||||||
| @ -616,6 +616,7 @@ | |||||||
|     "lineage-ingestion": "血缘关系提取", |     "lineage-ingestion": "血缘关系提取", | ||||||
|     "lineage-lowercase": "血缘", |     "lineage-lowercase": "血缘", | ||||||
|     "lineage-node-lowercase": "血缘关系节点", |     "lineage-node-lowercase": "血缘关系节点", | ||||||
|  |     "lineage-source": "Source of Lineage", | ||||||
|     "list": "列表", |     "list": "列表", | ||||||
|     "list-entity": "列出{{entity}}", |     "list-entity": "列出{{entity}}", | ||||||
|     "live": "实时", |     "live": "实时", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Harsh Vador
						Harsh Vador