From 1e50fc1376d3be341f45ef74ca7fe45d27f1aa26 Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Wed, 25 Jun 2025 20:54:07 +0530 Subject: [PATCH] fix(ui): version link and fetch logic with 1 hour wait time (#21932) * fix(ui): version link and fetch logic with 1 hour wait time * fix tests * fix test --- .../Modals/WhatsNewModal/whats-new-modal.less | 1 - .../ui/src/components/NavBar/NavBar.test.tsx | 58 ++++++++++++++++--- .../ui/src/components/NavBar/NavBar.tsx | 42 +++++++++----- .../ui/src/constants/Navbar.constants.ts | 4 +- .../ui/src/constants/URL.constants.ts | 3 + .../resources/ui/src/constants/constants.ts | 5 +- .../ui/src/utils/NavbarUtilClassBase.test.tsx | 4 +- .../ui/src/utils/NavbarUtils.test.tsx | 6 +- .../resources/ui/src/utils/NavbarUtils.tsx | 2 +- 9 files changed, 91 insertions(+), 34 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whats-new-modal.less b/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whats-new-modal.less index c83c2d6e73d..07ea434cda7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whats-new-modal.less +++ b/openmetadata-ui/src/main/resources/ui/src/components/Modals/WhatsNewModal/whats-new-modal.less @@ -124,7 +124,6 @@ .slick-active { button { background-color: @primary-color; - width: inherit; border-radius: 5px; } } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx index b0ee2238085..1b672f2efcb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.test.tsx @@ -12,7 +12,10 @@ */ import { act, render, screen } from '@testing-library/react'; import React from 'react'; -import { ONE_HOUR_MS } from '../../constants/constants'; +import { + LAST_VERSION_FETCH_TIME_KEY, + ONE_HOUR_MS, +} from '../../constants/constants'; import { HELP_ITEMS_ENUM } from '../../constants/Navbar.constants'; import { getVersion } from '../../rest/miscAPI'; import { getHelpDropdownItems } from '../../utils/NavbarUtils'; @@ -177,13 +180,13 @@ describe('Test NavBar Component', () => { }); }); -// --- Additional tests for fetchOMVersion one hour threshold --- -describe('fetchOMVersion one hour threshold', () => { +// --- Tests for handleDocumentVisibilityChange one hour threshold --- +describe('handleDocumentVisibilityChange one hour threshold', () => { const OLD_DATE_NOW = Date.now; beforeEach(() => { jest.resetModules(); - + jest.clearAllMocks(); global.Date.now = jest.fn(); }); @@ -191,7 +194,7 @@ describe('fetchOMVersion one hour threshold', () => { global.Date.now = OLD_DATE_NOW; }); - it('should NOT call getVersion if less than one hour since last fetch', async () => { + it('should NOT call getVersion on window focus if less than one hour since last fetch', async () => { const now = 2000000; const lastFetch = now - (ONE_HOUR_MS - 1000); // less than 1 hour ago mockGetItem.mockReturnValue(String(lastFetch)); @@ -200,26 +203,63 @@ describe('fetchOMVersion one hour threshold', () => { render(); await screen.findByTestId('global-search-bar'); + // Clear the initial getVersion call from mount + jest.clearAllMocks(); + + // Simulate window focus event + await act(async () => { + window.dispatchEvent(new Event('focus')); + }); + expect(getVersion).not.toHaveBeenCalled(); }); - it('should call getVersion and setItem if more than one hour since last fetch', async () => { + it('should call getVersion and setItem on window focus if more than one hour since last fetch', async () => { const now = 3000000; const lastFetch = now - (ONE_HOUR_MS + 1000); // more than 1 hour ago mockGetItem.mockReturnValue(String(lastFetch)); (global.Date.now as jest.Mock).mockReturnValue(now); render(); - await Promise.resolve(); + await screen.findByTestId('global-search-bar'); + // Clear the initial getVersion call from mount + jest.clearAllMocks(); + + // Simulate window focus event await act(async () => { - expect(getVersion).toHaveBeenCalled(); + window.dispatchEvent(new Event('focus')); }); + expect(getVersion).toHaveBeenCalled(); expect(mockSetItem).toHaveBeenCalledWith( - 'versionFetchTime', + LAST_VERSION_FETCH_TIME_KEY, '3000000', expect.objectContaining({ expires: expect.any(Date) }) ); }); + + it('should call getVersion on window focus if no previous fetch time exists', async () => { + const now = 4000000; + mockGetItem.mockReturnValue(null); // No previous fetch time + (global.Date.now as jest.Mock).mockReturnValue(now); + + render(); + await screen.findByTestId('global-search-bar'); + + // Clear the initial getVersion call from mount + jest.clearAllMocks(); + + // Simulate window focus event + await act(async () => { + window.dispatchEvent(new Event('focus')); + }); + + expect(getVersion).toHaveBeenCalled(); + expect(mockSetItem).toHaveBeenCalledWith( + LAST_VERSION_FETCH_TIME_KEY, + '4000000', + expect.objectContaining({ expires: expect.any(Date) }) + ); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx index bf80d818241..b1501f00e2e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/NavBar/NavBar.tsx @@ -45,10 +45,10 @@ import { ReactComponent as SidebarCollapsedIcon } from '../../assets/svg/ic-side import { ReactComponent as SidebarExpandedIcon } from '../../assets/svg/ic-sidebar-expanded.svg'; import { DEFAULT_DOMAIN_VALUE, + LAST_VERSION_FETCH_TIME_KEY, NOTIFICATION_READ_TIMER, ONE_HOUR_MS, SOCKET_EVENTS, - VERSION_FETCH_TIME_KEY, } from '../../constants/constants'; import { GlobalSettingsMenuCategory } from '../../constants/GlobalSettings.constants'; import { HELP_ITEMS_ENUM } from '../../constants/Navbar.constants'; @@ -130,22 +130,16 @@ const NavBar = () => { } = useCurrentUserPreferences(); const fetchOMVersion = async () => { - // If version fetch happens within an hour, skip fetching - const lastFetchTime = cookieStorage.getItem(VERSION_FETCH_TIME_KEY); - const now = Date.now(); - - if (lastFetchTime && now - Number(lastFetchTime) < ONE_HOUR_MS) { - // Less than an hour since last fetch, skip fetching - return; - } - try { const res = await getVersion(); - setVersion(res.version); - // Set/update the cookie with current time, expires in 1 hour - cookieStorage.setItem(VERSION_FETCH_TIME_KEY, String(now), { - expires: new Date(now + ONE_HOUR_MS), + + const now = Date.now(); + // Update the cache timestamp + cookieStorage.setItem(LAST_VERSION_FETCH_TIME_KEY, String(now), { + expires: new Date(Date.now() + ONE_HOUR_MS), }); + + setVersion(res.version); } catch (err) { showErrorToast( err as AxiosError, @@ -325,7 +319,27 @@ const NavBar = () => { ) { return; } + + // Check if we need to fetch based on cache timing + // This is to block the API call for 1 hour + const lastFetchTime = cookieStorage.getItem(LAST_VERSION_FETCH_TIME_KEY); + const now = Date.now(); + + if (lastFetchTime) { + const timeSinceLastFetch = now - parseInt(lastFetchTime); + if (timeSinceLastFetch < ONE_HOUR_MS) { + // Less than 1 hour since last fetch, skip API call + return; + } + } + const newVersion = await getVersion(); + + // Update the cache timestamp + cookieStorage.setItem(LAST_VERSION_FETCH_TIME_KEY, String(now), { + expires: new Date(Date.now() + ONE_HOUR_MS), + }); + // Compare version only if version is set previously to have fair comparison if (version && version !== newVersion.version) { setShowVersionMissMatchAlert(true); diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/Navbar.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/Navbar.constants.ts index 2c290582d6e..ae6f0370172 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/Navbar.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/Navbar.constants.ts @@ -21,7 +21,7 @@ import documentationLinksClassBase from '../utils/DocumentationLinksClassBase'; import i18n from '../utils/i18next/LocalUtil'; import { ROUTES } from './constants'; -import { URL_GITHUB_REPO, URL_JOIN_SLACK } from './URL.constants'; +import { URL_JOIN_SLACK, URL_OM_RELEASE_UPDATES } from './URL.constants'; export enum HELP_ITEMS_ENUM { TOUR = 'tour', @@ -80,7 +80,7 @@ export const HELP_ITEMS = [ key: HELP_ITEMS_ENUM.VERSION, label: i18n.t('label.version'), icon: IconVersionBlack, - link: URL_GITHUB_REPO, + link: URL_OM_RELEASE_UPDATES, isExternal: true, }, ]; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/URL.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/URL.constants.ts index c0fce6400c2..b386d1ce42b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/URL.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/URL.constants.ts @@ -14,3 +14,6 @@ export const URL_JOIN_SLACK = 'https://slack.open-metadata.org'; export const URL_OPEN_METADATA_DOCS = 'https://docs.open-metadata.org/'; export const URL_GITHUB_REPO = 'https://github.com/open-metadata/OpenMetadata'; + +export const URL_OM_RELEASE_UPDATES = + 'https://open-metadata.org/product-updates#{{currentVersion}}'; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts index eb8eb6c1519..12b4967773c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/constants.ts @@ -65,6 +65,8 @@ export const MAX_CHAR_LIMIT_ENTITY_SUMMARY = 130; export const TEST_CASE_FEED_GRAPH_HEIGHT = 250; export const ONE_MINUTE_IN_MILLISECOND = 60000; export const TWO_MINUTE_IN_MILLISECOND = 120000; +export const ONE_HOUR_MS = 3600000; // 1 hour in milliseconds +export const LAST_VERSION_FETCH_TIME_KEY = 'versionFetchTime'; export const LOCALSTORAGE_RECENTLY_VIEWED = `recentlyViewedData_${COOKIE_VERSION}`; export const LOCALSTORAGE_RECENTLY_SEARCHED = `recentlySearchedData_${COOKIE_VERSION}`; export const REDIRECT_PATHNAME = 'redirectUrlPath'; @@ -432,6 +434,3 @@ export const MAX_VISIBLE_OWNERS_FOR_FEED_TAB = 4; export const MAX_VISIBLE_OWNERS_FOR_FEED_CARD = 2; export const BREADCRUMB_SEPARATOR = '/'; - -export const VERSION_FETCH_TIME_KEY = 'versionFetchTime'; -export const ONE_HOUR_MS = 60 * 60 * 1000; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtilClassBase.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtilClassBase.test.tsx index 20becde6baf..c1b7a065200 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtilClassBase.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtilClassBase.test.tsx @@ -13,8 +13,8 @@ import { ROUTES } from '../constants/constants'; import { - URL_GITHUB_REPO, URL_JOIN_SLACK, + URL_OM_RELEASE_UPDATES, URL_OPEN_METADATA_DOCS, } from '../constants/URL.constants'; import navbarUtilClassBase from './NavbarUtilClassBase'; @@ -28,7 +28,7 @@ describe('NavbarUtilClassBase', () => { expect(stringifyResult).toContain(URL_OPEN_METADATA_DOCS); expect(stringifyResult).toContain(ROUTES.SWAGGER); expect(stringifyResult).toContain(URL_JOIN_SLACK); - expect(stringifyResult).toContain(URL_GITHUB_REPO); + expect(stringifyResult).toContain(URL_OM_RELEASE_UPDATES); expect(stringifyResult).toContain('label.tour'); expect(stringifyResult).toContain('label.doc-plural'); expect(stringifyResult).toContain('label.api-uppercase'); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.test.tsx index ddb7e3d7d88..1648547e6da 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.test.tsx @@ -13,7 +13,7 @@ import { ROUTES } from '../constants/constants'; import { HELP_ITEMS_ENUM } from '../constants/Navbar.constants'; -import { URL_GITHUB_REPO } from '../constants/URL.constants'; +import { URL_OM_RELEASE_UPDATES } from '../constants/URL.constants'; import { getHelpDropdownItems } from './NavbarUtils'; describe('NavbarUtils test', () => { @@ -30,7 +30,9 @@ describe('NavbarUtils test', () => { // Test external link const externalLink = helpDropdownItems[5].label.props.href; - expect(externalLink).toBe(URL_GITHUB_REPO); + expect(externalLink).toBe( + URL_OM_RELEASE_UPDATES.replace('{{currentVersion}}', version) + ); expect(helpDropdownItems[5].label.props.target).toBe('_blank'); expect(helpDropdownItems[5].label.props.rel).toBe('noreferrer'); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.tsx index d235d4e9267..d132febaa12 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/NavbarUtils.tsx @@ -56,7 +56,7 @@ const getHelpDropdownLabel = (item: SupportItem, version?: string) => { return ( {getHelpDropdownLabelContentRenderer(item, version)}