mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-11-04 12:36:23 +00:00 
			
		
		
		
	Fixes 23223: Mentions from incidents/entities not showing in Notifications (#23874)
* Fix: Notifications not showing the mentions from incident/entity * Fix: Fixed the unit test * Playwright: Added test for verifing incident manager mention notification * Fix: Addressed PR comments * Fix: fixed code smells * Playwright: Fixed the playwright tests --------- Co-authored-by: Ashish Gupta <ashish@getcollate.io>
This commit is contained in:
		
							parent
							
								
									73eca212ce
								
							
						
					
					
						commit
						c0df88f02f
					
				@ -536,8 +536,7 @@ test.describe('Mention notifications in Notification Box', () => {
 | 
				
			|||||||
        const mentionsFeedResponse = adminPage.waitForResponse(
 | 
					        const mentionsFeedResponse = adminPage.waitForResponse(
 | 
				
			||||||
          (response) =>
 | 
					          (response) =>
 | 
				
			||||||
            response.url().includes('/api/v1/feed') &&
 | 
					            response.url().includes('/api/v1/feed') &&
 | 
				
			||||||
            response.url().includes('filterType=MENTIONS') &&
 | 
					            response.url().includes('filterType=MENTIONS')
 | 
				
			||||||
            response.url().includes('type=Conversation')
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await mentionsTab.click();
 | 
					        await mentionsTab.click();
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@ import { SidebarItem } from '../../constant/sidebar';
 | 
				
			|||||||
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
 | 
					import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
 | 
				
			||||||
import { TableClass } from '../../support/entity/TableClass';
 | 
					import { TableClass } from '../../support/entity/TableClass';
 | 
				
			||||||
import { UserClass } from '../../support/user/UserClass';
 | 
					import { UserClass } from '../../support/user/UserClass';
 | 
				
			||||||
 | 
					import { addMentionCommentInFeed } from '../../utils/activityFeed';
 | 
				
			||||||
import { performAdminLogin } from '../../utils/admin';
 | 
					import { performAdminLogin } from '../../utils/admin';
 | 
				
			||||||
import { resetTokenFromBotPage } from '../../utils/bot';
 | 
					import { resetTokenFromBotPage } from '../../utils/bot';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -25,7 +26,7 @@ import {
 | 
				
			|||||||
  getApiContext,
 | 
					  getApiContext,
 | 
				
			||||||
  redirectToHomePage,
 | 
					  redirectToHomePage,
 | 
				
			||||||
} from '../../utils/common';
 | 
					} from '../../utils/common';
 | 
				
			||||||
import { addOwner } from '../../utils/entity';
 | 
					import { addOwner, waitForAllLoadersToDisappear } from '../../utils/entity';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  acknowledgeTask,
 | 
					  acknowledgeTask,
 | 
				
			||||||
  assignIncident,
 | 
					  assignIncident,
 | 
				
			||||||
@ -208,6 +209,27 @@ test.describe('Incident Manager', PLAYWRIGHT_INGESTION_TAG_OBJ, () => {
 | 
				
			|||||||
      await updateAssignee;
 | 
					      await updateAssignee;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await test.step(
 | 
				
			||||||
 | 
					      'Verify that notifications correctly display mentions for the incident manager',
 | 
				
			||||||
 | 
					      async () => {
 | 
				
			||||||
 | 
					        const testcaseName = await page
 | 
				
			||||||
 | 
					          .getByTestId('entity-header-name')
 | 
				
			||||||
 | 
					          .innerText();
 | 
				
			||||||
 | 
					        await addMentionCommentInFeed(page, 'admin', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await adminPage.waitForLoadState('networkidle');
 | 
				
			||||||
 | 
					        await waitForAllLoadersToDisappear(adminPage);
 | 
				
			||||||
 | 
					        await adminPage.getByRole('button', { name: 'Notifications' }).click();
 | 
				
			||||||
 | 
					        await adminPage.getByText('Mentions').click();
 | 
				
			||||||
 | 
					        await adminPage.waitForLoadState('networkidle');
 | 
				
			||||||
 | 
					        await waitForAllLoadersToDisappear(adminPage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await expect(adminPage.getByLabel('Mentions')).toContainText(
 | 
				
			||||||
 | 
					          `mentioned you on the testCase ${testcaseName}`
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await test.step(
 | 
					    await test.step(
 | 
				
			||||||
      "Re-assign incident from test case page's header",
 | 
					      "Re-assign incident from test case page's header",
 | 
				
			||||||
      async () => {
 | 
					      async () => {
 | 
				
			||||||
 | 
				
			|||||||
@ -128,14 +128,6 @@ export const addMentionCommentInFeed = async (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
 | 
					  await page.waitForSelector('[data-testid="loader"]', { state: 'detached' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const firstMessage = getNthFeedMessage(page, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  await expect(firstMessage).toBeVisible();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  await firstMessage.locator('[data-testid="reply-count"]').click();
 | 
					 | 
				
			||||||
  await page.waitForSelector('.ant-drawer-content', {
 | 
					 | 
				
			||||||
    state: 'visible',
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
  await page.getByTestId('comments-input-field').click();
 | 
					  await page.getByTestId('comments-input-field').click();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const userSuggestionsResponse = page.waitForResponse(
 | 
					  const userSuggestionsResponse = page.waitForResponse(
 | 
				
			||||||
 | 
				
			|||||||
@ -103,7 +103,7 @@ export const addOwner = async ({
 | 
				
			|||||||
  await page.getByTestId(initiatorId).click();
 | 
					  await page.getByTestId(initiatorId).click();
 | 
				
			||||||
  if (type === 'Users') {
 | 
					  if (type === 'Users') {
 | 
				
			||||||
    const userListResponse = page.waitForResponse(
 | 
					    const userListResponse = page.waitForResponse(
 | 
				
			||||||
      '/api/v1/search/query?q=&index=user_search_index&*'
 | 
					      '/api/v1/search/query?q=*&index=user_search_index&*'
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    await page.getByRole('tab', { name: type }).click();
 | 
					    await page.getByRole('tab', { name: type }).click();
 | 
				
			||||||
    await userListResponse;
 | 
					    await userListResponse;
 | 
				
			||||||
 | 
				
			|||||||
@ -52,11 +52,7 @@ import { useTourProvider } from '../../context/TourProvider/TourProvider';
 | 
				
			|||||||
import { useWebSocketConnector } from '../../context/WebSocketProvider/WebSocketProvider';
 | 
					import { useWebSocketConnector } from '../../context/WebSocketProvider/WebSocketProvider';
 | 
				
			||||||
import { EntityTabs, EntityType } from '../../enums/entity.enum';
 | 
					import { EntityTabs, EntityType } from '../../enums/entity.enum';
 | 
				
			||||||
import { EntityReference } from '../../generated/entity/type';
 | 
					import { EntityReference } from '../../generated/entity/type';
 | 
				
			||||||
import {
 | 
					import { BackgroundJob, JobType } from '../../generated/jobs/backgroundJob';
 | 
				
			||||||
  BackgroundJob,
 | 
					 | 
				
			||||||
  EnumCleanupArgs,
 | 
					 | 
				
			||||||
  JobType,
 | 
					 | 
				
			||||||
} from '../../generated/jobs/backgroundJob';
 | 
					 | 
				
			||||||
import { useCurrentUserPreferences } from '../../hooks/currentUserStore/useCurrentUserStore';
 | 
					import { useCurrentUserPreferences } from '../../hooks/currentUserStore/useCurrentUserStore';
 | 
				
			||||||
import { useApplicationStore } from '../../hooks/useApplicationStore';
 | 
					import { useApplicationStore } from '../../hooks/useApplicationStore';
 | 
				
			||||||
import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation';
 | 
					import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation';
 | 
				
			||||||
@ -248,7 +244,7 @@ const NavBar = () => {
 | 
				
			|||||||
        const { jobArgs, status, jobType } = backgroundJobData;
 | 
					        const { jobArgs, status, jobType } = backgroundJobData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (jobType === JobType.CustomPropertyEnumCleanup) {
 | 
					        if (jobType === JobType.CustomPropertyEnumCleanup) {
 | 
				
			||||||
          const enumCleanupArgs = jobArgs as EnumCleanupArgs;
 | 
					          const enumCleanupArgs = jobArgs;
 | 
				
			||||||
          if (!enumCleanupArgs.entityType) {
 | 
					          if (!enumCleanupArgs.entityType) {
 | 
				
			||||||
            showErrorToast(
 | 
					            showErrorToast(
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
@ -280,11 +276,11 @@ const NavBar = () => {
 | 
				
			|||||||
      icon: Logo,
 | 
					      icon: Logo,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    notification.onclick = () => {
 | 
					    notification.onclick = () => {
 | 
				
			||||||
      const isChrome = window.navigator.userAgent.indexOf('Chrome');
 | 
					      const isChrome = globalThis.navigator.userAgent.indexOf('Chrome');
 | 
				
			||||||
      // Applying logic to open a new window onclick of browser notification from chrome
 | 
					      // Applying logic to open a new window onclick of browser notification from chrome
 | 
				
			||||||
      // As it does not open the concerned tab by default.
 | 
					      // As it does not open the concerned tab by default.
 | 
				
			||||||
      if (isChrome > -1) {
 | 
					      if (isChrome > -1) {
 | 
				
			||||||
        window.open(path);
 | 
					        globalThis.open(path);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        navigate(path);
 | 
					        navigate(path);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -317,7 +313,7 @@ const NavBar = () => {
 | 
				
			|||||||
      const now = Date.now();
 | 
					      const now = Date.now();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (lastFetchTime) {
 | 
					      if (lastFetchTime) {
 | 
				
			||||||
        const timeSinceLastFetch = now - parseInt(lastFetchTime);
 | 
					        const timeSinceLastFetch = now - Number.parseInt(lastFetchTime);
 | 
				
			||||||
        if (timeSinceLastFetch < ONE_HOUR_MS) {
 | 
					        if (timeSinceLastFetch < ONE_HOUR_MS) {
 | 
				
			||||||
          // Less than 1 hour since last fetch, skip API call
 | 
					          // Less than 1 hour since last fetch, skip API call
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
@ -532,6 +528,7 @@ const NavBar = () => {
 | 
				
			|||||||
              className="cursor-pointer"
 | 
					              className="cursor-pointer"
 | 
				
			||||||
              dropdownRender={() => (
 | 
					              dropdownRender={() => (
 | 
				
			||||||
                <NotificationBox
 | 
					                <NotificationBox
 | 
				
			||||||
 | 
					                  activeTab={activeTab}
 | 
				
			||||||
                  hasMentionNotification={hasMentionNotification}
 | 
					                  hasMentionNotification={hasMentionNotification}
 | 
				
			||||||
                  hasTaskNotification={hasTaskNotification}
 | 
					                  hasTaskNotification={hasTaskNotification}
 | 
				
			||||||
                  onMarkMentionsNotificationRead={
 | 
					                  onMarkMentionsNotificationRead={
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,7 @@ import { getFilters, tabsInfo } from './NotificationBox.utils';
 | 
				
			|||||||
import NotificationFeedCard from './NotificationFeedCard.component';
 | 
					import NotificationFeedCard from './NotificationFeedCard.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NotificationBox = ({
 | 
					const NotificationBox = ({
 | 
				
			||||||
 | 
					  activeTab,
 | 
				
			||||||
  hasMentionNotification,
 | 
					  hasMentionNotification,
 | 
				
			||||||
  hasTaskNotification,
 | 
					  hasTaskNotification,
 | 
				
			||||||
  onMarkTaskNotificationRead,
 | 
					  onMarkTaskNotificationRead,
 | 
				
			||||||
@ -71,9 +72,10 @@ const NotificationBox = ({
 | 
				
			|||||||
      // For mention notifications, get the actual user who made the mention from posts
 | 
					      // For mention notifications, get the actual user who made the mention from posts
 | 
				
			||||||
      let actualUser = mainFeed.from;
 | 
					      let actualUser = mainFeed.from;
 | 
				
			||||||
      let actualTimestamp = mainFeed.postTs;
 | 
					      let actualTimestamp = mainFeed.postTs;
 | 
				
			||||||
 | 
					      let feedType = feed.type || ThreadType.Conversation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (
 | 
					      if (
 | 
				
			||||||
        feed.type === ThreadType.Conversation &&
 | 
					        activeTab === ThreadType.Conversation &&
 | 
				
			||||||
        feed.posts &&
 | 
					        feed.posts &&
 | 
				
			||||||
        feed.posts.length > 0
 | 
					        feed.posts.length > 0
 | 
				
			||||||
      ) {
 | 
					      ) {
 | 
				
			||||||
@ -85,9 +87,10 @@ const NotificationBox = ({
 | 
				
			|||||||
          )
 | 
					          )
 | 
				
			||||||
          .sort((a, b) => (b.postTs ?? 0) - (a.postTs ?? 0))[0];
 | 
					          .sort((a, b) => (b.postTs ?? 0) - (a.postTs ?? 0))[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (mentionPost && mentionPost.postTs !== undefined) {
 | 
					        if (mentionPost?.postTs !== undefined) {
 | 
				
			||||||
          actualUser = mentionPost.from;
 | 
					          actualUser = mentionPost.from;
 | 
				
			||||||
          actualTimestamp = mentionPost.postTs;
 | 
					          actualTimestamp = mentionPost.postTs;
 | 
				
			||||||
 | 
					          feedType = ThreadType.Conversation;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -96,7 +99,7 @@ const NotificationBox = ({
 | 
				
			|||||||
          createdBy={actualUser}
 | 
					          createdBy={actualUser}
 | 
				
			||||||
          entityFQN={entityFQN as string}
 | 
					          entityFQN={entityFQN as string}
 | 
				
			||||||
          entityType={entityType as string}
 | 
					          entityType={entityType as string}
 | 
				
			||||||
          feedType={feed.type || ThreadType.Conversation}
 | 
					          feedType={feedType}
 | 
				
			||||||
          key={`${actualUser} ${mainFeed.id}`}
 | 
					          key={`${actualUser} ${mainFeed.id}`}
 | 
				
			||||||
          task={feed}
 | 
					          task={feed}
 | 
				
			||||||
          timestamp={actualTimestamp}
 | 
					          timestamp={actualTimestamp}
 | 
				
			||||||
@ -106,8 +109,8 @@ const NotificationBox = ({
 | 
				
			|||||||
  }, [notifications]);
 | 
					  }, [notifications]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getNotificationData = (
 | 
					  const getNotificationData = (
 | 
				
			||||||
    threadType: ThreadType,
 | 
					    feedFilter: FeedFilter,
 | 
				
			||||||
    feedFilter: FeedFilter
 | 
					    threadType?: ThreadType
 | 
				
			||||||
  ) => {
 | 
					  ) => {
 | 
				
			||||||
    setIsLoading(true);
 | 
					    setIsLoading(true);
 | 
				
			||||||
    getFeedsWithFilter(currentUser?.id, feedFilter, undefined, threadType)
 | 
					    getFeedsWithFilter(currentUser?.id, feedFilter, undefined, threadType)
 | 
				
			||||||
@ -132,7 +135,7 @@ const NotificationBox = ({
 | 
				
			|||||||
      onTabChange(key);
 | 
					      onTabChange(key);
 | 
				
			||||||
      const { threadType, feedFilter } = getFilters(key as ThreadType);
 | 
					      const { threadType, feedFilter } = getFilters(key as ThreadType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      getNotificationData(threadType, feedFilter);
 | 
					      getNotificationData(feedFilter, threadType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      setViewAllPath(
 | 
					      setViewAllPath(
 | 
				
			||||||
        getUserPath(
 | 
					        getUserPath(
 | 
				
			||||||
@ -156,7 +159,7 @@ const NotificationBox = ({
 | 
				
			|||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  useEffect(() => {
 | 
					  useEffect(() => {
 | 
				
			||||||
    getNotificationData(ThreadType.Task, FeedFilter.ASSIGNED_TO);
 | 
					    getNotificationData(FeedFilter.ASSIGNED_TO, ThreadType.Task);
 | 
				
			||||||
  }, []);
 | 
					  }, []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const getTabTitle = (name: string, key: string) => {
 | 
					  const getTabTitle = (name: string, key: string) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface NotificationBoxProp {
 | 
					export interface NotificationBoxProp {
 | 
				
			||||||
 | 
					  activeTab: string;
 | 
				
			||||||
  hasMentionNotification: boolean;
 | 
					  hasMentionNotification: boolean;
 | 
				
			||||||
  hasTaskNotification: boolean;
 | 
					  hasTaskNotification: boolean;
 | 
				
			||||||
  onMarkMentionsNotificationRead: () => void;
 | 
					  onMarkMentionsNotificationRead: () => void;
 | 
				
			||||||
 | 
				
			|||||||
@ -61,6 +61,7 @@ const mockShowErrorToast = jest.fn();
 | 
				
			|||||||
const mockOnMarkTaskNotificationRead = jest.fn();
 | 
					const mockOnMarkTaskNotificationRead = jest.fn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mockProps = {
 | 
					const mockProps = {
 | 
				
			||||||
 | 
					  activeTab: ThreadType.Task,
 | 
				
			||||||
  hasMentionNotification: true,
 | 
					  hasMentionNotification: true,
 | 
				
			||||||
  hasTaskNotification: true,
 | 
					  hasTaskNotification: true,
 | 
				
			||||||
  onMarkMentionsNotificationRead: jest.fn(),
 | 
					  onMarkMentionsNotificationRead: jest.fn(),
 | 
				
			||||||
 | 
				
			|||||||
@ -47,7 +47,7 @@ export const tabsInfo = [
 | 
				
			|||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getFilters = (activeTab: ThreadType) => ({
 | 
					export const getFilters = (activeTab: ThreadType) => ({
 | 
				
			||||||
  threadType: activeTab,
 | 
					  threadType: activeTab === ThreadType.Task ? activeTab : undefined,
 | 
				
			||||||
  feedFilter:
 | 
					  feedFilter:
 | 
				
			||||||
    activeTab === ThreadType.Task
 | 
					    activeTab === ThreadType.Task
 | 
				
			||||||
      ? FeedFilter.ASSIGNED_TO
 | 
					      ? FeedFilter.ASSIGNED_TO
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user