mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-27 10:26:09 +00:00
GEN-712: show column lineage function in edge drawer (#18032)
* show column lineage function * add e2e tests
This commit is contained in:
parent
9219dbe5c1
commit
47cde167b4
@ -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();
|
||||
}
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1390,3 +1390,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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user