Repository: provectus/kafka-ui
Issue: Air Gap: Enhancement: Opt out of version check
Labels: type/enhancement
scope/backend
scope/frontend
status/pending-frontend

Hello,

i'm using your great UI for kafka cluster instance and it's perfect : functionality, rapidity, installating in k8s world with helm.
But i try to install this UI in isolated network without internet access so github.com is not allow.

Is it possible to disable call from the ui to https://api.github.com/repos/provectus/kafka-ui/releases/latest and load the font inside the image ?
Like that the UI could be use in isolated network.



Issue comments:
Hello there LaurentDanti! 👋

Thank you and congratulations 🎉 for opening your very first issue in this project! 💖

In case you want to claim this issue, please comment down below! We will try to get back to you as soon as we can. 👀
@LaurentDanti thank you for the issue!

@Haarolean I think we already have issue created for fonts, maybe we add github call handling to that issue? 
hey @LaurentDanti, thanks for reaching out.
I know already at least a few cases in which people successfully use the app in the "air gap" environment (e.g. #1390).
We already have the fonts issue in progress (#2728), we'll keep this open for considering opting out of the version check.
@Haarolean  hello, I also have the fonts issue, UI doesn't work in isolated network, would you give  workaround ?
@Fuou we're working on it (#2372)
 @Haarolean what's the status here?
@cassanellicarlo it's up for grabs for the 3rd-party contributors since we don't have enough capacity to address this issue.
Hey 🙏 what is the status woth the opt out option?
Frontend implementation is invalid.

Tagged versions before:
<img width="361" alt="image" src="https://github.com/provectus/kafka-ui/assets/1494347/c44b0863-63dd-446f-a25a-a5d816843a60">
Tagged versions now are missing the tag and have a commit displayed anyway:
<img width="408" alt="image" src="https://github.com/provectus/kafka-ui/assets/1494347/ed5fb888-52fa-4f8a-aa18-9d0d768d1402">

if version == versionTag display version rather than commitId
<img width="766" alt="image" src="https://github.com/provectus/kafka-ui/assets/1494347/66a9a277-d452-46df-ac17-2696fa8bba87">

@David-DB88 



ok


Pull request review comments:
why did we remove this ?
<img width="612" alt="Screen Shot 2023-04-20 at 19 55 05" src="https://user-images.githubusercontent.com/58771979/233421166-3898ea13-58b8-44a0-b081-63734d97ea59.png">
i changed it with this 

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/Version/Version.tsx b/kafka-ui-react-app/src/components/Version/Version.tsx
index 7a820b116a2..6788605d92b 100644
--- a/kafka-ui-react-app/src/components/Version/Version.tsx
+++ b/kafka-ui-react-app/src/components/Version/Version.tsx
@@ -1,52 +1,38 @@
 import React from 'react';
 import WarningIcon from 'components/common/Icons/WarningIcon';
 import { gitCommitPath } from 'lib/paths';
-import { useActuatorInfo } from 'lib/hooks/api/actuatorInfo';
-import { BUILD_VERSION_PATTERN } from 'lib/constants';
 import { useLatestVersion } from 'lib/hooks/api/latestVersion';
 import { formatTimestamp } from 'lib/dateTimeHelpers';
 
 import * as S from './Version.styled';
-import compareVersions from './compareVersions';
 
 const Version: React.FC = () => {
-  const { data: actuatorInfo = {} } = useActuatorInfo();
   const { data: latestVersionInfo = {} } = useLatestVersion();
-
-  const tag = actuatorInfo?.build?.version;
-  const commit = actuatorInfo?.git?.commit.id;
-  const { tag_name: latestTag } = latestVersionInfo;
-
-  const outdated = compareVersions(tag, latestTag);
-
-  const currentVersion = tag?.match(BUILD_VERSION_PATTERN)
-    ? tag
-    : formatTimestamp(actuatorInfo?.build?.time);
-
-  if (!tag) return null;
+  const { buildTime, commitId, isLatestRelease } = latestVersionInfo.build;
+  const { versionTag } = latestVersionInfo?.latestRelease || '';
 
   return (
     <S.Wrapper>
-      {!!outdated && (
+      {!isLatestRelease && (
         <S.OutdatedWarning
-          title={`Your app version is outdated. Current latest version is ${latestTag}`}
+          title={`Your app version is outdated. Current latest version is ${versionTag}`}
         >
           <WarningIcon />
         </S.OutdatedWarning>
       )}
 
-      {commit && (
+      {commitId && (
         <div>
           <S.CurrentCommitLink
             title="Current commit"
             target="__blank"
-            href={gitCommitPath(commit)}
+            href={gitCommitPath(commitId)}
           >
-            {commit}
+            {commitId}
           </S.CurrentCommitLink>
         </div>
       )}
-      <S.CurrentVersion>{currentVersion}</S.CurrentVersion>
+      <S.CurrentVersion>{formatTimestamp(buildTime)}</S.CurrentVersion>
     </S.Wrapper>
   );
 };
diff --git a/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx b/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx
index d407966058c..2700dac8947 100644
--- a/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx
+++ b/kafka-ui-react-app/src/components/Version/__tests__/Version.spec.tsx
@@ -2,87 +2,40 @@ import React from 'react';
 import { screen } from '@testing-library/dom';
 import Version from 'components/Version/Version';
 import { render } from 'lib/testHelpers';
-import { formatTimestamp } from 'lib/dateTimeHelpers';
-import { useActuatorInfo } from 'lib/hooks/api/actuatorInfo';
 import { useLatestVersion } from 'lib/hooks/api/latestVersion';
-import { actuatorInfoPayload } from 'lib/fixtures/actuatorInfo';
-import { latestVersionPayload } from 'lib/fixtures/latestVersion';
+import {
+  deprecatedVersionPayload,
+  latestVersionPayload,
+} from 'lib/fixtures/latestVersion';
 
-jest.mock('lib/hooks/api/actuatorInfo', () => ({
-  useActuatorInfo: jest.fn(),
-}));
 jest.mock('lib/hooks/api/latestVersion', () => ({
   useLatestVersion: jest.fn(),
 }));
-
 describe('Version Component', () => {
-  const versionTag = 'v0.5.0';
-  const snapshotTag = 'test-SNAPSHOT';
-  const commitTag = 'befd3b328e2c9c7df57b0c5746561b2f7fee8813';
-
-  const actuatorVersionPayload = actuatorInfoPayload(versionTag);
-  const formattedTimestamp = formatTimestamp(actuatorVersionPayload.build.time);
-
-  beforeEach(() => {
-    (useActuatorInfo as jest.Mock).mockImplementation(() => ({
-      data: actuatorVersionPayload,
-    }));
-    (useLatestVersion as jest.Mock).mockImplementation(() => ({
-      data: latestVersionPayload,
-    }));
-  });
-
-  describe('tag does not exist', () => {
-    it('does not render component', async () => {
-      (useActuatorInfo as jest.Mock).mockImplementation(() => ({
-        data: null,
-      }));
-      const { container } = render(<Version />);
-      expect(container.firstChild).toBeEmptyDOMElement();
-    });
-  });
+  const commitId = '96a577a';
 
-  describe('renders current version', () => {
-    it('renders release build version as current version', async () => {
-      render(<Version />);
-      expect(screen.getByText(versionTag)).toBeInTheDocument();
-    });
-    it('renders formatted timestamp as current version when version is commit', async () => {
-      (useActuatorInfo as jest.Mock).mockImplementation(() => ({
-        data: actuatorInfoPayload(commitTag),
+  describe('render latest version', () => {
+    beforeEach(() => {
+      (useLatestVersion as jest.Mock).mockImplementation(() => ({
+        data: latestVersionPayload,
       }));
-      render(<Version />);
-      expect(screen.getByText(formattedTimestamp)).toBeInTheDocument();
     });
-    it('renders formatted timestamp as current version when version contains -SNAPSHOT', async () => {
-      (useActuatorInfo as jest.Mock).mockImplementation(() => ({
-        data: actuatorInfoPayload(snapshotTag),
-      }));
+    it('renders latest release version as current version', async () => {
       render(<Version />);
-      expect(screen.getByText(formattedTimestamp)).toBeInTheDocument();
+      expect(screen.getByText(commitId)).toBeInTheDocument();
     });
-  });
 
-  describe('outdated build version', () => {
-    it('renders warning message', async () => {
-      (useActuatorInfo as jest.Mock).mockImplementation(() => ({
-        data: actuatorInfoPayload('v0.3.0'),
-      }));
+    it('should not show warning icon if it is last release', async () => {
       render(<Version />);
-      expect(
-        screen.getByTitle(
-          `Your app version is outdated. Current latest version is ${latestVersionPayload.tag_name}`
-        )
-      ).toBeInTheDocument();
+      expect(screen.queryByRole('img')).not.toBeInTheDocument();
     });
   });
 
-  describe('current commit id with link', () => {
-    it('renders', async () => {
-      render(<Version />);
-      expect(
-        screen.getByText(actuatorVersionPayload.git.commit.id)
-      ).toBeInTheDocument();
-    });
+  it('show warning icon if it is not last release', async () => {
+    (useLatestVersion as jest.Mock).mockImplementation(() => ({
+      data: deprecatedVersionPayload,
+    }));
+    render(<Version />);
+    expect(screen.getByRole('img')).toBeInTheDocument();
   });
 });
diff --git a/kafka-ui-react-app/src/components/common/Icons/WarningIcon.tsx b/kafka-ui-react-app/src/components/common/Icons/WarningIcon.tsx
index ab2f8dee759..1bffe0db537 100644
--- a/kafka-ui-react-app/src/components/common/Icons/WarningIcon.tsx
+++ b/kafka-ui-react-app/src/components/common/Icons/WarningIcon.tsx
@@ -13,6 +13,7 @@ const WarningIcon: React.FC = () => {
   return (
     <WarningIconContainer>
       <svg
+        role="img"
         width="14"
         height="13"
         viewBox="0 0 14 13"
diff --git a/kafka-ui-react-app/src/lib/fixtures/actuatorInfo.ts b/kafka-ui-react-app/src/lib/fixtures/actuatorInfo.ts
deleted file mode 100644
index e7dff6e1eca..00000000000
--- a/kafka-ui-react-app/src/lib/fixtures/actuatorInfo.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export const actuatorInfoPayload = (
-  version = 'befd3b328e2c9c7df57b0c5746561b2f7fee8813'
-) => ({
-  git: { commit: { id: 'befd3b3' } },
-  build: {
-    artifact: 'kafka-ui-api',
-    name: 'kafka-ui-api',
-    time: '2022-09-15T09:52:21.753Z',
-    version,
-    group: 'com.provectus',
-  },
-});
diff --git a/kafka-ui-react-app/src/lib/fixtures/latestVersion.ts b/kafka-ui-react-app/src/lib/fixtures/latestVersion.ts
index a6c9eca856e..d1e62da6b80 100644
--- a/kafka-ui-react-app/src/lib/fixtures/latestVersion.ts
+++ b/kafka-ui-react-app/src/lib/fixtures/latestVersion.ts
@@ -1,3 +1,16 @@
+export const deprecatedVersionPayload = {
+  build: {
+    buildTime: '2023-04-14T09:47:35.463Z',
+    commitId: '96a577a',
+    isLatestRelease: false,
+    version: '96a577a98c6069376c5d22ed49cffd3739f1bbdc',
+  },
+};
 export const latestVersionPayload = {
-  tag_name: 'v0.4.0',
+  build: {
+    buildTime: '2023-04-14T09:47:35.463Z',
+    commitId: '96a577a',
+    isLatestRelease: true,
+    version: '96a577a98c6069376c5d22ed49cffd3739f1bbdc',
+  },
 };
diff --git a/kafka-ui-react-app/src/lib/hooks/api/__tests__/actuatorInfo.spec.ts b/kafka-ui-react-app/src/lib/hooks/api/__tests__/actuatorInfo.spec.ts
deleted file mode 100644
index c4e639680ed..00000000000
--- a/kafka-ui-react-app/src/lib/hooks/api/__tests__/actuatorInfo.spec.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import fetchMock from 'fetch-mock';
-import * as hooks from 'lib/hooks/api/actuatorInfo';
-import { expectQueryWorks, renderQueryHook } from 'lib/testHelpers';
-import { actuatorInfoPayload } from 'lib/fixtures/actuatorInfo';
-
-const actuatorInfoPath = '/actuator/info';
-
-describe('Actuator info hooks', () => {
-  beforeEach(() => fetchMock.restore());
-  describe('useActuatorInfo', () => {
-    it('returns the correct data', async () => {
-      const mock = fetchMock.getOnce(actuatorInfoPath, actuatorInfoPayload());
-      const { result } = renderQueryHook(() => hooks.useActuatorInfo());
-      await expectQueryWorks(mock, result);
-    });
-  });
-});
diff --git a/kafka-ui-react-app/src/lib/hooks/api/__tests__/latestVersion.spec.ts b/kafka-ui-react-app/src/lib/hooks/api/__tests__/latestVersion.spec.ts
index 0770bd4606d..a12f2629950 100644
--- a/kafka-ui-react-app/src/lib/hooks/api/__tests__/latestVersion.spec.ts
+++ b/kafka-ui-react-app/src/lib/hooks/api/__tests__/latestVersion.spec.ts
@@ -1,18 +1,16 @@
 import fetchMock from 'fetch-mock';
 import { expectQueryWorks, renderQueryHook } from 'lib/testHelpers';
-import * as hooks from 'lib/hooks/api/latestVersion';
-import { GIT_REPO_LATEST_RELEASE_LINK } from 'lib/constants';
 import { latestVersionPayload } from 'lib/fixtures/latestVersion';
+import { useLatestVersion } from 'lib/hooks/api/latestVersion';
+
+const latestVersionPath = '/api/info';
 
 describe('Latest version hooks', () => {
   beforeEach(() => fetchMock.restore());
   describe('useLatestVersion', () => {
     it('returns the correct data', async () => {
-      const mock = fetchMock.getOnce(
-        GIT_REPO_LATEST_RELEASE_LINK,
-        latestVersionPayload
-      );
-      const { result } = renderQueryHook(() => hooks.useLatestVersion());
+      const mock = fetchMock.getOnce(latestVersionPath, latestVersionPayload);
+      const { result } = renderQueryHook(() => useLatestVersion());
       await expectQueryWorks(mock, result);
     });
   });
diff --git a/kafka-ui-react-app/src/lib/hooks/api/actuatorInfo.ts b/kafka-ui-react-app/src/lib/hooks/api/actuatorInfo.ts
deleted file mode 100644
index 7e1835d9079..00000000000
--- a/kafka-ui-react-app/src/lib/hooks/api/actuatorInfo.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { useQuery } from '@tanstack/react-query';
-import { BASE_PARAMS, QUERY_REFETCH_OFF_OPTIONS } from 'lib/constants';
-
-const fetchActuatorInfo = async () => {
-  const data = await fetch(
-    `${BASE_PARAMS.basePath}/actuator/info`,
-    BASE_PARAMS
-  ).then((res) => res.json());
-
-  return data;
-};
-
-export function useActuatorInfo() {
-  return useQuery(
-    ['actuatorInfo'],
-    fetchActuatorInfo,
-    QUERY_REFETCH_OFF_OPTIONS
-  );
-}
diff --git a/kafka-ui-react-app/src/lib/hooks/api/latestVersion.ts b/kafka-ui-react-app/src/lib/hooks/api/latestVersion.ts
index 1087fb572e1..0711ad34d91 100644
--- a/kafka-ui-react-app/src/lib/hooks/api/latestVersion.ts
+++ b/kafka-ui-react-app/src/lib/hooks/api/latestVersion.ts
@@ -1,21 +1,19 @@
 import { useQuery } from '@tanstack/react-query';
-import {
-  QUERY_REFETCH_OFF_OPTIONS,
-  GIT_REPO_LATEST_RELEASE_LINK,
-} from 'lib/constants';
+import { BASE_PARAMS, QUERY_REFETCH_OFF_OPTIONS } from 'lib/constants';
 
-const fetchLatestVersion = async () => {
-  const data = await fetch(GIT_REPO_LATEST_RELEASE_LINK).then((res) =>
-    res.json()
-  );
+const fetchLatestVersionInfo = async () => {
+  const data = await fetch(
+    `${BASE_PARAMS.basePath}/api/info`,
+    BASE_PARAMS
+  ).then((res) => res.json());
 
   return data;
 };
 
 export function useLatestVersion() {
   return useQuery(
-    ['latestVersion'],
-    fetchLatestVersion,
+    ['versionInfo'],
+    fetchLatestVersionInfo,
     QUERY_REFETCH_OFF_OPTIONS
   );
 }


Test patch: