From f038e9fbf71e0e6228c75f962d7004e777bd5cc8 Mon Sep 17 00:00:00 2001 From: karanh37 Date: Fri, 27 Sep 2024 19:40:59 +0530 Subject: [PATCH 1/2] show column lineage function --- .../EdgeInfoDrawer.component.tsx | 10 ++- .../ui/src/utils/EntityLineageUtils.test.tsx | 73 ++++++++++++++++++- .../ui/src/utils/EntityLineageUtils.tsx | 12 +++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx index 24b82ddf22c7..83799b3bcf46 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx @@ -27,6 +27,7 @@ import { EntityType } from '../../../enums/entity.enum'; import { Source } from '../../../generated/type/entityLineage'; import { getNameFromFQN } from '../../../utils/CommonUtils'; import { + getColumnFunctionValue, getColumnSourceTargetHandles, getLineageDetailsObject, } from '../../../utils/EntityLineageUtils'; @@ -66,6 +67,7 @@ const EdgeInfoDrawer = ({ const { source, target, data } = edge; const { sourceHandle, targetHandle } = getColumnSourceTargetHandles(edge); const { pipeline, pipelineEntityType } = data?.edge ?? {}; + const isColumnLineage = sourceHandle && targetHandle; let sourceData: Node | undefined, targetData: Node | undefined; nodes.forEach((node) => { @@ -121,7 +123,13 @@ const EdgeInfoDrawer = ({ }, functionInfo: { key: t('label.function'), - value: data.columnFunctionValue, + value: isColumnLineage + ? getColumnFunctionValue( + data?.edge?.columns ?? [], + sourceHandle ?? '', + targetHandle ?? '' + ) + : undefined, }, }); setIsLoading(false); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.test.tsx index 42cc95e4e8d6..e5f584daa603 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.test.tsx @@ -16,7 +16,7 @@ import { EdgeTypeEnum } from '../components/Entity/EntityLineage/EntityLineage.i import { EdgeDetails } from '../components/Lineage/Lineage.interface'; import { SourceType } from '../components/SearchedData/SearchedData.interface'; import { EntityType } from '../enums/entity.enum'; -import { AddLineage } from '../generated/api/lineage/addLineage'; +import { AddLineage, ColumnLineage } from '../generated/api/lineage/addLineage'; import { MOCK_CHILD_MAP, MOCK_LINEAGE_DATA_NEW, @@ -29,6 +29,7 @@ import { createNewEdge, getAllTracedEdges, getChildMap, + getColumnFunctionValue, getColumnLineageData, getColumnSourceTargetHandles, getConnectedNodesEdges, @@ -608,4 +609,74 @@ describe('Test EntityLineageUtils utility', () => { expect(result.childrenHeading).toEqual('label.column-plural'); }); }); + + describe('getColumnFunctionValue', () => { + it('should return the correct function value when a matching column is found', () => { + const columns = [ + { + toColumn: 'targetColumn', + fromColumns: ['sourceColumn'], + function: 'SUM', + }, + { + toColumn: 'anotherTargetColumn', + fromColumns: ['anotherSourceColumn'], + function: 'AVG', + }, + ]; + const sourceFqn = 'sourceColumn'; + const targetFqn = 'targetColumn'; + + const result = getColumnFunctionValue(columns, sourceFqn, targetFqn); + + expect(result).toBe('SUM'); + }); + + it('should return undefined when no matching column is found', () => { + const columns = [ + { + toColumn: 'targetColumn', + fromColumns: ['sourceColumn'], + function: 'SUM', + }, + { + toColumn: 'anotherTargetColumn', + fromColumns: ['anotherSourceColumn'], + function: 'AVG', + }, + ]; + const sourceFqn = 'nonExistentSourceColumn'; + const targetFqn = 'nonExistentTargetColumn'; + + const result = getColumnFunctionValue(columns, sourceFqn, targetFqn); + + expect(result).toBeUndefined(); + }); + + it('should return undefined when columns array is empty', () => { + const columns: ColumnLineage[] = []; + const sourceFqn = 'sourceColumn'; + const targetFqn = 'targetColumn'; + + const result = getColumnFunctionValue(columns, sourceFqn, targetFqn); + + expect(result).toBeUndefined(); + }); + + it('should return undefined when fromColumns is undefined', () => { + const columns = [ + { + toColumn: 'targetColumn', + fromColumns: undefined, + function: 'SUM', + }, + ]; + const sourceFqn = 'sourceColumn'; + const targetFqn = 'targetColumn'; + + const result = getColumnFunctionValue(columns, sourceFqn, targetFqn); + + expect(result).toBeUndefined(); + }); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx index fd80600900b1..a76cf7a5772f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx @@ -1387,3 +1387,15 @@ export const getPaginatedChildMap = ( return { nodes, edges }; }; + +export const getColumnFunctionValue = ( + columns: ColumnLineage[], + sourceFqn: string, + targetFqn: string +) => { + const column = columns.find( + (col) => col.toColumn === targetFqn && col.fromColumns?.includes(sourceFqn) + ); + + return column?.function; +}; From ee21d47c324e8e063a5f80d327a750b3f3a3b37b Mon Sep 17 00:00:00 2001 From: karanh37 Date: Mon, 30 Sep 2024 13:34:44 +0530 Subject: [PATCH 2/2] add e2e tests --- .../ui/playwright/e2e/Pages/Lineage.spec.ts | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Lineage.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Lineage.spec.ts index 11f75bfb5f73..058784b928c9 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Lineage.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Lineage.spec.ts @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import test from '@playwright/test'; +import test, { expect } from '@playwright/test'; import { get } from 'lodash'; import { ApiEndpointClass } from '../../support/entity/ApiEndpointClass'; import { ContainerClass } from '../../support/entity/ContainerClass'; @@ -280,3 +280,85 @@ test('Verify column lineage between table and api endpoint', async ({ await afterAction(); }); + +test('Verify function data in edge drawer', async ({ browser }) => { + const { page } = await createNewPage(browser); + const { apiContext, afterAction } = await getApiContext(page); + const table1 = new TableClass(); + const table2 = new TableClass(); + + try { + await table1.create(apiContext); + await table2.create(apiContext); + const sourceTableFqn = get(table1, 'entityResponseData.fullyQualifiedName'); + const sourceColName = `${sourceTableFqn}.${get( + table1, + 'entityResponseData.columns[0].name' + )}`; + + const targetTableFqn = get(table2, 'entityResponseData.fullyQualifiedName'); + const targetColName = `${targetTableFqn}.${get( + table2, + 'entityResponseData.columns[0].name' + )}`; + + await addPipelineBetweenNodes(page, table1, table2); + await activateColumnLayer(page); + await addColumnLineage(page, sourceColName, targetColName); + + const lineageReq = page.waitForResponse('/api/v1/lineage/getLineage?*'); + await page.reload(); + const lineageRes = await lineageReq; + const jsonRes = await lineageRes.json(); + const edge = jsonRes.edges[0]; + const columnData = edge.columns[0]; + + const newEdge = { + edge: { + fromEntity: { + id: edge.fromEntity.id, + type: edge.fromEntity.type, + }, + toEntity: { + id: edge.toEntity.id, + type: edge.toEntity.type, + }, + lineageDetails: { + columnsLineage: [ + { + fromColumns: [columnData.fromColumns[0]], + function: 'count', + toColumn: columnData.toColumn, + }, + ], + description: 'test', + }, + }, + }; + await apiContext.put(`/api/v1/lineage`, { + data: newEdge, + }); + const lineageReq1 = page.waitForResponse('/api/v1/lineage/getLineage?*'); + await page.reload(); + await lineageReq1; + + await activateColumnLayer(page); + await page + .locator( + `[data-testid="column-edge-${btoa(sourceColName)}-${btoa( + targetColName + )}"]` + ) + .dispatchEvent('click'); + + await page.locator('.edge-info-drawer').isVisible(); + + await expect(await page.locator('[data-testid="Function"]')).toContainText( + 'count' + ); + } finally { + await table1.delete(apiContext); + await table2.delete(apiContext); + await afterAction(); + } +});