From 66dfc9d375f72c1f6cf4b40a7d228012db94ba92 Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Sat, 23 Aug 2025 22:01:46 +0530 Subject: [PATCH] MINOR: fix the knowledge, Glossary/Term page redirect notification link (#21341) * fix the knowledge page redirect notification link * fix the feed redirection for glossary * use entityRef data for displayName if present (cherry picked from commit 8d4b7c78947678e151ff448a77f63bf2eb00a23d) --- .../NotificationFeedCard.component.tsx | 16 ++- .../NotificationFeedCard.test.tsx | 121 +++++++++++++++++- .../main/resources/ui/src/utils/FeedUtils.tsx | 3 +- 3 files changed, 132 insertions(+), 8 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx index 669428937ad..ce1449937e1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.component.tsx @@ -21,7 +21,7 @@ import { formatDateTime, getRelativeTime, } from '../../utils/date-time/DateTimeUtils'; -import { getEntityLinkFromType } from '../../utils/EntityUtils'; +import { getEntityLinkFromType, getEntityName } from '../../utils/EntityUtils'; import { entityDisplayName, prepareFeedLink } from '../../utils/FeedUtils'; import Fqn from '../../utils/Fqn'; import { getTaskDetailPath } from '../../utils/TasksUtils'; @@ -84,6 +84,12 @@ const NotificationFeedCard: FC = ({ ); }, [entityType, task, taskDetails]); + const entityName = useMemo(() => { + return task?.entityRef + ? getEntityName(task?.entityRef) + : entityDisplayName(entityType, entityFQN); + }, [task, entityType, entityFQN]); + return ( = ({ avatar={} className="m-0" description={ - + @@ -107,8 +116,9 @@ const NotificationFeedCard: FC = ({ {entityType} - {entityDisplayName(entityType, entityFQN)} + {entityName} ) : ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx index e475b9c1021..68e48741cb4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/NotificationBox/NotificationFeedCard.test.tsx @@ -13,6 +13,7 @@ import { act, render, screen } from '@testing-library/react'; import React from 'react'; import { ThreadType } from '../../generated/api/feed/createThread'; +import { Thread } from '../../generated/entity/feed/thread'; import NotificationFeedCard from './NotificationFeedCard.component'; jest.mock('../../utils/date-time/DateTimeUtils', () => ({ @@ -40,21 +41,63 @@ jest.mock('react-router-dom', () => ({ })); jest.mock('../../utils/EntityUtils', () => ({ getEntityLinkFromType: jest.fn().mockReturnValue('/mock-entity-link'), + getEntityName: jest + .fn() + .mockImplementation(({ displayName, name }) => displayName || name || ''), })); + jest.mock('../../utils/Fqn', () => ({ split: jest.fn().mockReturnValue(['mockGlossary']), })); const mockThread = { - about: 'test', id: '33873393-bd68-46e9-bccc-7701c1c41ad6', - message: 'f93d08e9-2d38-4d01-a294-f8b44fbb0f4a', + type: 'Conversation', + href: 'http://host.docker.internal:8585/v1/feed/b41ef8d2-e369-4fce-b106-8f000258e361', + threadTs: 1755772414483, + about: '<#E::page::Article_sQDEeTK6::description>', + entityRef: { + id: 'eda48fe4-515f-44ee-8afc-f7e4ef01277a', + type: 'page', + name: 'Article_sQDEeTK6', + fullyQualifiedName: 'Article_sQDEeTK6', + description: '', + displayName: 'SACHIN', + }, + generatedBy: 'user', + cardStyle: 'default', + fieldOperation: 'updated', + createdBy: 'admin', + updatedAt: 1755772414483, + updatedBy: 'admin', + resolved: false, + task: { + id: 16, + type: 'RequestTestCaseFailureResolution', + assignees: [ + { + id: '9311f065-e150-4948-96a4-e98906443b37', + type: 'user', + name: 'admin', + fullyQualifiedName: 'admin', + displayName: 'admin', + deleted: false, + }, + ], + status: 'Open', + testCaseResolutionStatusId: '29c0871d-bd96-431b-8823-e968316915af', + }, + message: + '<#E::user::admin|[@admin](http://localhost:3000/users/admin)> Hii!', + postsCount: 0, + posts: [], + reactions: [], }; const mockProps = { createdBy: 'admin', entityType: 'task', entityFQN: 'test', - task: mockThread, + task: mockThread as Thread, feedType: ThreadType.Task, }; @@ -66,4 +109,76 @@ describe('Test NotificationFeedCard Component', () => { expect(await screen.findByText('ProfilePicture')).toBeInTheDocument(); }); + + it('renders assigned task message and link for ThreadType.Task', async () => { + await act(async () => { + render(); + }); + + expect( + screen.getByText(/assigned-you-a-new-task-lowercase/i) + ).toBeInTheDocument(); + + expect( + screen.getByText(`#${mockThread.task.id} ${mockThread.task.type}`) + ).toBeInTheDocument(); + }); + + it('renders mentioned message and entity link for ThreadType.Conversation', async () => { + const conversationProps = { + ...mockProps, + feedType: ThreadType.Conversation, + }; + await act(async () => { + render(); + }); + + expect( + screen.getByText(/mentioned-you-on-the-lowercase/i) + ).toBeInTheDocument(); + + expect(screen.getByText(conversationProps.entityType)).toBeInTheDocument(); + }); + + it('should renders entityRef data is available', async () => { + const conversationProps = { + ...mockProps, + feedType: ThreadType.Conversation, + }; + await act(async () => { + render(); + }); + + expect( + screen.getByText(mockThread.entityRef.displayName) + ).toBeInTheDocument(); + }); + + it('should renders default entityName by entityDisplayName if entityRef not present', async () => { + const conversationProps = { + ...mockProps, + task: { + ...mockProps.task, + entityRef: undefined, + }, + feedType: ThreadType.Conversation, + }; + await act(async () => { + render(); + }); + + expect(screen.getByText('database.schema.table')).toBeInTheDocument(); + }); + + it('renders timestamp', async () => { + const timestampProps = { + ...mockProps, + timestamp: 1692612000000, // Example: 2023-08-21T10:00:00Z in ms + }; + await act(async () => { + render(); + }); + + expect(screen.getByText('1692612000000')).toBeInTheDocument(); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx index f42d626aa2c..1f76639b118 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/FeedUtils.tsx @@ -501,9 +501,8 @@ export const updateThreadData = async ( export const prepareFeedLink = (entityType: string, entityFQN: string) => { const withoutFeedEntities = [ EntityType.WEBHOOK, - EntityType.GLOSSARY, - EntityType.GLOSSARY_TERM, EntityType.TYPE, + EntityType.KNOWLEDGE_PAGE, ]; const entityLink = entityUtilClassBase.getEntityLink(entityType, entityFQN);