Skip to content

Commit 6d953d8

Browse files
authored
ui: add lineage section in overview tab in right panel (#24768)
* ui: add lineage section in overview tab in right panel * add tests * fix failing tests * fix loader * add tests around loader * fix failing specs * fix loader issue * address copilot comment * address comments * fix infinite loader issue * address comments
1 parent 5c7b81c commit 6d953d8

32 files changed

Lines changed: 1016 additions & 28 deletions

openmetadata-ui/src/main/resources/ui/playwright/e2e/Flow/RightEntityPanelFlow.spec.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,18 @@ test.describe('Right Entity Panel - Admin User Flow', () => {
475475

476476
test('Lineage Tab - No Lineage', async ({ adminPage }) => {
477477
const summaryPanel = adminPage.locator('.entity-summary-panel-container');
478+
479+
const lineageSection = summaryPanel.locator(
480+
'[data-testid="lineage-section"]'
481+
);
482+
483+
if (await lineageSection.isVisible()) {
484+
await expect(
485+
summaryPanel.getByText(/no lineage connections found/i)
486+
).toBeVisible();
487+
}
488+
489+
// Now test the Lineage tab
478490
const lineageTab = summaryPanel.getByRole('menuitem', {
479491
name: /lineage/i,
480492
});
@@ -512,6 +524,51 @@ test.describe('Right Entity Panel - Admin User Flow', () => {
512524
await navigateToExploreAndSelectTable(adminPage);
513525

514526
const summaryPanel = adminPage.locator('.entity-summary-panel-container');
527+
528+
// First verify the Overview tab shows lineage counts in LineageSection
529+
await summaryPanel.getByRole('menuitem', {
530+
name: /overview/i,
531+
}).click();
532+
533+
await adminPage.waitForSelector('[data-testid="loader"]', {
534+
state: 'detached',
535+
});
536+
537+
const lineageSection = summaryPanel.locator(
538+
'[data-testid="upstream-lineage"]'
539+
);
540+
541+
if (await lineageSection.isVisible()) {
542+
// Verify upstream and downstream counts are visible
543+
const upstreamText = summaryPanel.getByText(/upstream:/i);
544+
const downstreamText = summaryPanel.getByText(/downstream:/i);
545+
546+
await expect(upstreamText).toBeVisible();
547+
await expect(downstreamText).toBeVisible();
548+
549+
// Verify the actual counts (should be 1 upstream and 1 downstream)
550+
const upstreamCountElement = summaryPanel.locator(
551+
'[data-testid="upstream-count"]'
552+
);
553+
const downstreamCountElement = summaryPanel.locator(
554+
'[data-testid="downstream-count"]'
555+
);
556+
557+
await expect(upstreamCountElement).toHaveText('1');
558+
await expect(downstreamCountElement).toHaveText('1');
559+
560+
// Click on lineage section to navigate to lineage page
561+
await lineageSection.click();
562+
563+
// Verify navigation to lineage route
564+
await adminPage.waitForURL(/.*\/lineage$/);
565+
expect(adminPage.url()).toContain('/lineage');
566+
567+
// Navigate back to explore and reopen the entity panel
568+
await navigateToExploreAndSelectTable(adminPage);
569+
}
570+
571+
// Now test the Lineage tab
515572
const lineageTab = summaryPanel.getByRole('menuitem', {
516573
name: /lineage/i,
517574
});

openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,7 @@ export const checkExploreSearchFilter = async (
19861986
const queryRes = page.waitForResponse(querySearchURL);
19871987
await page.click('[data-testid="update-btn"]');
19881988
await queryRes;
1989-
await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
1989+
await waitForAllLoadersToDisappear(page);
19901990

19911991
await expect(
19921992
page.getByTestId(

openmetadata-ui/src/main/resources/ui/playwright/utils/entityPanel.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ export const editTags = async (
9494
.locator('[data-testid="selectable-list"]')
9595
.scrollIntoViewIfNeeded();
9696

97-
const searchTagResponse = page.waitForResponse(
98-
`/api/v1/search/query?q=*${encodeURIComponent(
99-
tagName
100-
)}*index=tag_search_index*`
101-
);
97+
const searchTagResponse = page.waitForResponse(
98+
`/api/v1/search/query?q=*${encodeURIComponent(
99+
tagName
100+
)}*index=tag_search_index*`
101+
);
102102
const searchBar = page.locator('[data-testid="tag-select-search-bar"]');
103103
await searchBar.fill(tagName);
104104
await searchTagResponse;

openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* limitations under the License.
1212
*/
1313
import { EntityTags } from 'Models';
14+
import { LineageData } from '../../components/Lineage/Lineage.interface';
1415
import { EntityType } from '../../enums/entity.enum';
1516
import { DataProduct } from '../../generated/entity/domains/dataProduct';
1617
import { EntityReference } from '../../generated/type/entityReference';
@@ -30,6 +31,8 @@ export type DataAssetSummaryPanelProps = {
3031
entityType: EntityType;
3132
isDomainVisible?: boolean;
3233
isLineageView?: boolean;
34+
lineageData?: LineageData | null;
35+
isLineageLoading?: boolean;
3336
onOwnerUpdate?: (updatedOwners: EntityReference[]) => void;
3437
onDomainUpdate?: (updatedDomains: EntityReference[]) => void;
3538
onTierUpdate?: (updatedTier?: TagLabel) => void;
@@ -38,6 +41,7 @@ export type DataAssetSummaryPanelProps = {
3841
onGlossaryTermsUpdate?: (updatedGlossaryTerms: TagLabel[]) => void;
3942
onDescriptionUpdate?: (updatedDescription: string) => void;
4043
onLinkClick?: () => void;
44+
onLineageClick?: () => void;
4145
};
4246

4347
export interface TestCaseStatusCounts {

openmetadata-ui/src/main/resources/ui/src/components/DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.tsx

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* See the License for the specific language governing permissions and
1111
* limitations under the License.
1212
*/
13-
import { isEmpty } from 'lodash';
13+
import { isEmpty, isNil } from 'lodash';
1414
import { useCallback, useEffect, useMemo, useState } from 'react';
1515
import { useTranslation } from 'react-i18next';
1616
import { usePermissionProvider } from '../../context/PermissionProvider/PermissionProvider';
@@ -19,15 +19,16 @@ import {
1919
ResourceEntity,
2020
} from '../../context/PermissionProvider/PermissionProvider.interface';
2121
import { useTourProvider } from '../../context/TourProvider/TourProvider';
22-
import {
23-
getCurrentMillis,
24-
getEpochMillisForPastDays,
25-
} from '../../utils/date-time/DateTimeUtils';
22+
import { getUpstreamDownstreamNodesEdges } from '../../utils/EntityLineageUtils';
2623
import { getEntityChildDetails } from '../../utils/EntitySummaryPanelUtils';
2724
import {
2825
DRAWER_NAVIGATION_OPTIONS,
2926
getEntityOverview,
3027
} from '../../utils/EntityUtils';
28+
import {
29+
getCurrentMillis,
30+
getEpochMillisForPastDays,
31+
} from '../../utils/date-time/DateTimeUtils';
3132

3233
import { AxiosError } from 'axios';
3334
import { Operation } from 'fast-json-patch';
@@ -45,21 +46,23 @@ import { fetchCharts } from '../../utils/DashboardDetailsUtils';
4546
import entityUtilClassBase from '../../utils/EntityUtilClassBase';
4647
import { generateEntityLink, getTierTags } from '../../utils/TableUtils';
4748
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
49+
import {
50+
DataAssetSummaryPanelProps,
51+
TestCaseStatusCounts,
52+
} from '../DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.interface';
53+
import { ENTITY_RIGHT_PANEL_LINEAGE_TABS } from '../Entity/EntityRightPanel/EntityRightPanelVerticalNav.constants';
4854
import DataProductsSection from '../common/DataProductsSection/DataProductsSection';
4955
import DataQualitySection from '../common/DataQualitySection/DataQualitySection';
5056
import DescriptionSection from '../common/DescriptionSection/DescriptionSection';
5157
import DomainsSection from '../common/DomainsSection/DomainsSection';
5258
import GlossaryTermsSection from '../common/GlossaryTermsSection/GlossaryTermsSection';
59+
import LineageSection from '../common/LineageSection/LineageSection';
5360
import Loader from '../common/Loader/Loader';
5461
import OverviewSection from '../common/OverviewSection/OverviewSection';
5562
import OwnersSection from '../common/OwnersSection/OwnersSection';
5663
import SummaryPanelSkeleton from '../common/Skeleton/SummaryPanelSkeleton/SummaryPanelSkeleton.component';
5764
import TagsSection from '../common/TagsSection/TagsSection';
5865
import TierSection from '../common/TierSection/TierSection';
59-
import {
60-
DataAssetSummaryPanelProps,
61-
TestCaseStatusCounts,
62-
} from '../DataAssetSummaryPanelV1/DataAssetSummaryPanelV1.interface';
6366

6467
export const DataAssetSummaryPanelV1 = ({
6568
dataAsset,
@@ -77,6 +80,9 @@ export const DataAssetSummaryPanelV1 = ({
7780
onGlossaryTermsUpdate,
7881
onDescriptionUpdate,
7982
onLinkClick,
83+
lineageData,
84+
isLineageLoading = false,
85+
onLineageClick,
8086
}: DataAssetSummaryPanelProps) => {
8187
const { t } = useTranslation();
8288
const { getEntityPermission } = usePermissionProvider();
@@ -164,6 +170,48 @@ export const DataAssetSummaryPanelV1 = ({
164170
);
165171
}, [dataAsset, entityType, highlights, charts, chartsDetailsLoading]);
166172

173+
const { upstreamCount, downstreamCount, shouldShowLineageSection } =
174+
useMemo(() => {
175+
if (!ENTITY_RIGHT_PANEL_LINEAGE_TABS.includes(entityType)) {
176+
return {
177+
upstreamCount: 0,
178+
downstreamCount: 0,
179+
shouldShowLineageSection: false,
180+
};
181+
}
182+
183+
if (
184+
isLineageLoading ||
185+
isNil(lineageData) ||
186+
isEmpty(dataAsset.fullyQualifiedName)
187+
) {
188+
return {
189+
upstreamCount: 0,
190+
downstreamCount: 0,
191+
shouldShowLineageSection: true,
192+
};
193+
}
194+
195+
const nodes = Object.values(lineageData.nodes).map((node) => node.entity);
196+
const edges = [
197+
...Object.values(lineageData.upstreamEdges),
198+
...Object.values(lineageData.downstreamEdges),
199+
];
200+
201+
const { upstreamNodes, downstreamNodes } =
202+
getUpstreamDownstreamNodesEdges(
203+
edges,
204+
nodes,
205+
dataAsset.fullyQualifiedName!
206+
);
207+
208+
return {
209+
upstreamCount: upstreamNodes.length,
210+
downstreamCount: downstreamNodes.length,
211+
shouldShowLineageSection: true,
212+
};
213+
}, [lineageData, entityType, dataAsset.fullyQualifiedName, isLineageLoading]);
214+
167215
const fetchIncidentCount = useCallback(async () => {
168216
if (
169217
dataAsset?.fullyQualifiedName &&
@@ -445,6 +493,14 @@ export const DataAssetSummaryPanelV1 = ({
445493
/>
446494
)
447495
)}
496+
{shouldShowLineageSection && (
497+
<LineageSection
498+
downstreamCount={downstreamCount}
499+
isLoading={isLineageLoading}
500+
upstreamCount={upstreamCount}
501+
onLineageClick={onLineageClick}
502+
/>
503+
)}
448504
<div>
449505
<OwnersSection
450506
entityId={dataAsset.id}

0 commit comments

Comments
 (0)