Skip to content

feat(Clusters): correct links with relative path in balancer #2121

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/containers/Clusters/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -27,9 +27,11 @@ export const CLUSTERS_COLUMNS: Column<PreparedCluster>[] = [
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
Expand Down Expand Up @@ -81,7 +83,12 @@ export const CLUSTERS_COLUMNS: Column<PreparedCluster>[] = [
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);

Expand All @@ -99,8 +106,6 @@ export const CLUSTERS_COLUMNS: Column<PreparedCluster>[] = [
};
});

const backend = balancer && removeViewerPathname(balancer);

return (
preparedVersions.length > 0 && (
<ExternalLink
Expand Down
1 change: 1 addition & 0 deletions src/store/reducers/clusters/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {ExtendedMetaClusterVersion} from '../../../utils/clusterVersionColo

export interface PreparedCluster extends MetaExtendedClusterInfo {
preparedVersions: ExtendedMetaClusterVersion[];
preparedBackend?: string;
}

export interface ClusterDataAggregation {
Expand Down
4 changes: 4 additions & 0 deletions src/store/reducers/clusters/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getVersionMap,
prepareClusterVersions,
} from '../../../utils/clusterVersionColors';
import {prepareBackendFromBalancer} from '../../../utils/parseBalancer';

import type {PreparedCluster} from './types';

Expand All @@ -24,5 +25,8 @@ export const prepareClustersData = (data: MetaClusters): PreparedCluster[] => {
return clusters.map((cluster) => ({
...cluster,
preparedVersions: prepareClusterVersions(cluster.versions, versionToColor),
preparedBackend: cluster.balancer
? prepareBackendFromBalancer(cluster.balancer)
: undefined,
}));
};
74 changes: 73 additions & 1 deletion src/utils/__test__/parseBalancer.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
getCleanBalancerValue,
parseBalancer,
prepareBackendFromBalancer,
removePort,
removeProtocol,
removeViewerPathname,
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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);
});
});
2 changes: 1 addition & 1 deletion src/utils/__test__/prepareBackend.test.ts
Original file line number Diff line number Diff line change
@@ -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 =
Expand Down
27 changes: 25 additions & 2 deletions src/utils/parseBalancer.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand Down Expand Up @@ -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;
}
Loading