diff --git a/src/containers/Clusters/columns.tsx b/src/containers/Clusters/columns.tsx index ff089d485..00f42623c 100644 --- a/src/containers/Clusters/columns.tsx +++ b/src/containers/Clusters/columns.tsx @@ -10,7 +10,7 @@ import {UserCard} from '../../components/User/User'; import type {PreparedCluster} from '../../store/reducers/clusters/types'; import {formatStorageValuesToTb} from '../../utils/dataFormatters/dataFormatters'; import {createDeveloperUIMonitoringPageHref} from '../../utils/developerUI/developerUI'; -import {getCleanBalancerValue, removeViewerPathname} from '../../utils/parseBalancer'; +import {getCleanBalancerValue} from '../../utils/parseBalancer'; import {clusterTabsIds, getClusterPath} from '../Cluster/utils'; import {COLUMNS_NAMES, COLUMNS_TITLES} from './constants'; @@ -27,9 +27,11 @@ export const CLUSTERS_COLUMNS: Column[] = [ header: COLUMNS_TITLES[COLUMNS_NAMES.TITLE], width: 230, render: ({row}) => { - const {balancer, name: clusterName, use_embedded_ui: useEmbeddedUi} = row; - - const backend = balancer && removeViewerPathname(balancer); + const { + name: clusterName, + use_embedded_ui: useEmbeddedUi, + preparedBackend: backend, + } = row; const clusterPath = useEmbeddedUi && backend @@ -81,7 +83,12 @@ export const CLUSTERS_COLUMNS: Column[] = [ return versions[0] || undefined; }, render: ({row}) => { - const {preparedVersions, versions = [], balancer, name: clusterName} = row; + const { + preparedVersions, + versions = [], + name: clusterName, + preparedBackend: backend, + } = row; const hasErrors = !versions.length || versions.some((item) => !item.version); @@ -99,8 +106,6 @@ export const CLUSTERS_COLUMNS: Column[] = [ }; }); - const backend = balancer && removeViewerPathname(balancer); - return ( preparedVersions.length > 0 && ( { return clusters.map((cluster) => ({ ...cluster, preparedVersions: prepareClusterVersions(cluster.versions, versionToColor), + preparedBackend: cluster.balancer + ? prepareBackendFromBalancer(cluster.balancer) + : undefined, })); }; diff --git a/src/utils/__test__/parseBalancer.test.ts b/src/utils/__test__/parseBalancer.test.ts index 940cb7d80..91cf52846 100644 --- a/src/utils/__test__/parseBalancer.test.ts +++ b/src/utils/__test__/parseBalancer.test.ts @@ -1,6 +1,7 @@ import { getCleanBalancerValue, parseBalancer, + prepareBackendFromBalancer, removePort, removeProtocol, removeViewerPathname, @@ -68,12 +69,17 @@ describe('removeViewerPathname', () => { }); }); describe('removeProtocol', () => { - test('should remove protocol', () => { + test('should remove protocol from start', () => { const initialValue = 'https://ydb-testing-0000.search.net:8765/viewer/json'; const result = 'ydb-testing-0000.search.net:8765/viewer/json'; expect(removeProtocol(initialValue)).toBe(result); }); + test('should not remove protocol string in the middle', () => { + const initialValue = 'proxy/host/https:ydb-testing-0000.search.net'; + + expect(removeProtocol(initialValue)).toBe(initialValue); + }); }); describe('removePort', () => { test('should remove port', () => { @@ -92,3 +98,69 @@ describe('getCleanBalancerValue', () => { expect(getCleanBalancerValue(initialValue)).toBe(result); }); }); +describe('prepareBackendFromBalancer', () => { + const windowSpy = jest.spyOn(window, 'window', 'get'); + + afterEach(() => { + windowSpy.mockClear(); + }); + afterAll(() => { + windowSpy.mockRestore(); + }); + + test('should not change full balancer value - only remove viewer pathname', () => { + const initialValue = 'https://ydb-testing-0000.search.net:8765/viewer/json'; + const result = 'https://ydb-testing-0000.search.net:8765'; + + expect(prepareBackendFromBalancer(initialValue)).toBe(result); + }); + + test('should add meta backend for relative balancer value', () => { + const initialValue = '/proxy/host/ydb-testing-0000.search.net/viewer/json'; + const result = 'https://my-host.ru/proxy/host/ydb-testing-0000.search.net'; + + windowSpy.mockImplementation(() => { + return { + meta_backend: 'https://my-host.ru', + } as Window & typeof globalThis; + }); + + expect(prepareBackendFromBalancer(initialValue)).toBe(result); + }); + test('should add relative meta backend for relative balancer value', () => { + const initialValue = '/proxy/host/ydb-testing-0000.search.net/viewer/json'; + const result = '/meta/proxy/host/ydb-testing-0000.search.net'; + + windowSpy.mockImplementation(() => { + return { + meta_backend: '/meta', + } as Window & typeof globalThis; + }); + + expect(prepareBackendFromBalancer(initialValue)).toBe(result); + }); + test('should not add empty meta backend for relative balancer value', () => { + const initialValue = '/proxy/host/ydb-testing-0000.search.net/viewer/json'; + const result = '/proxy/host/ydb-testing-0000.search.net'; + + windowSpy.mockImplementation(() => { + return { + meta_backend: '', + } as Window & typeof globalThis; + }); + + expect(prepareBackendFromBalancer(initialValue)).toBe(result); + }); + test('should not add undefined meta backend for relative balancer value', () => { + const initialValue = '/proxy/host/ydb-testing-0000.search.net/viewer/json'; + const result = '/proxy/host/ydb-testing-0000.search.net'; + + windowSpy.mockImplementation(() => { + return { + meta_backend: undefined, + } as Window & typeof globalThis; + }); + + expect(prepareBackendFromBalancer(initialValue)).toBe(result); + }); +}); diff --git a/src/utils/__test__/prepareBackend.test.ts b/src/utils/__test__/prepareBackend.test.ts index 215b4b405..d9cf2e9ac 100644 --- a/src/utils/__test__/prepareBackend.test.ts +++ b/src/utils/__test__/prepareBackend.test.ts @@ -1,7 +1,7 @@ import {getBackendFromNodeHost, getBackendFromRawNodeData, prepareHost} from '../prepareBackend'; describe('prepareHost', () => { - test('should add vm prefix to cloud din nodes', () => { + test('should add u- prefix to cloud din nodes', () => { const cloudDinNodeInitialHost = 'vm-cc8mco0j0snqehgh7r2a-ru-central1-c-nlmw-aniq.cc8mco0j0snqehgh7r2a.ydb.mdb.cloud-preprod.net'; const cloudDinNodeResultHost = diff --git a/src/utils/parseBalancer.ts b/src/utils/parseBalancer.ts index d86d35ab7..2008fba9c 100644 --- a/src/utils/parseBalancer.ts +++ b/src/utils/parseBalancer.ts @@ -1,8 +1,11 @@ +const protocolRegex = /^http[s]?:\/\//; +const viewerPathnameRegex = /\/viewer\/json$/; + export const removeViewerPathname = (value: string) => { - return value.replace(/\/viewer\/json/, ''); + return value.replace(viewerPathnameRegex, ''); }; export const removeProtocol = (value: string) => { - return value.replace(/http[s]?:\/\//, ''); + return value.replace(protocolRegex, ''); }; export const removePort = (value: string) => { @@ -47,3 +50,23 @@ export const parseBalancer = (rawBalancer: string): ParsedBalancer => { export const getCleanBalancerValue = (rawBalancer: string) => { return removePort(parseBalancer(rawBalancer).balancer); }; + +export function prepareBackendFromBalancer(rawBalancer: string) { + const preparedBalancer = removeViewerPathname(rawBalancer); + + // Test if balancer starts with protocol + // It means it is a full url and it can be used as it is + // Otherwise it is a relative path to the current meta backend + if (protocolRegex.test(rawBalancer)) { + return preparedBalancer; + } + + // Use meta_backend if it is defined to form backend url + if (window.meta_backend) { + const path = window.meta_backend + '/' + preparedBalancer; + // Prevent multiple slashes in case meta_backend ends with slash or balancer starts with slash + return path.replaceAll(/([^:])(\/\/+)/g, '$1/'); + } + + return preparedBalancer; +}