diff --git a/src/components/VDisk/utils.ts b/src/components/VDisk/utils.ts index f738878b81..39b66829d9 100644 --- a/src/components/VDisk/utils.ts +++ b/src/components/VDisk/utils.ts @@ -1,31 +1,26 @@ -import {getDefaultNodePath} from '../../containers/Node/NodePages'; import {getVDiskPagePath} from '../../routes'; -import type {TVDiskStateInfo, TVSlotId} from '../../types/api/vdisk'; import {valueIsDefined} from '../../utils'; -import {stringifyVdiskId} from '../../utils/dataFormatters/dataFormatters'; -import {isFullVDiskData} from '../../utils/disks/helpers'; +import type {PreparedVDisk} from '../../utils/disks/types'; -export function getVDiskLink(data: TVDiskStateInfo | TVSlotId) { +export function getVDiskLink(data: PreparedVDisk) { let vDiskPath: string | undefined; - const isFullData = isFullVDiskData(data); - const VDiskSlotId = isFullData ? data.VDiskSlotId : data.VSlotId; - if ( - valueIsDefined(VDiskSlotId) && + valueIsDefined(data.VDiskSlotId) && valueIsDefined(data.PDiskId) && valueIsDefined(data.NodeId) ) { - vDiskPath = getVDiskPagePath(VDiskSlotId, data.PDiskId, data.NodeId); - } else if (valueIsDefined(data.NodeId) && isFullVDiskData(data)) { - vDiskPath = getDefaultNodePath( - data.NodeId, - { - pdiskId: data.PDiskId?.toString(), - vdiskId: stringifyVdiskId(data.VDiskId), - }, - 'structure', - ); + vDiskPath = getVDiskPagePath({ + vDiskSlotId: data.VDiskSlotId, + pDiskId: data.PDiskId, + nodeId: data.NodeId, + }); + } else if (valueIsDefined(data.StringifiedId)) { + vDiskPath = getVDiskPagePath({ + vDiskId: data.StringifiedId, + pDiskId: data.PDiskId, + nodeId: data.NodeId, + }); } return vDiskPath; diff --git a/src/components/VDiskInfo/VDiskInfo.tsx b/src/components/VDiskInfo/VDiskInfo.tsx index cf71b2cf91..ad2a82615c 100644 --- a/src/components/VDiskInfo/VDiskInfo.tsx +++ b/src/components/VDiskInfo/VDiskInfo.tsx @@ -154,7 +154,11 @@ export function VDiskInfo({ const links: React.ReactNode[] = []; if (withVDiskPageLink) { - const vDiskPagePath = getVDiskPagePath(VDiskSlotId, PDiskId, NodeId); + const vDiskPagePath = getVDiskPagePath({ + vDiskSlotId: VDiskSlotId, + pDiskId: PDiskId, + nodeId: NodeId, + }); links.push( withDeveloperUILink && valueIsDefined(NodeId) && valueIsDefined(PDiskId) && - valueIsDefined(VDiskSlotId) + (valueIsDefined(VDiskSlotId) || valueIsDefined(StringifiedId)) ) { - const vDiskInternalViewerPath = createVDiskDeveloperUILink({ - nodeId: NodeId, - pDiskId: PDiskId, - vDiskSlotId: VDiskSlotId, - }); - - const vDiskPagePath = getVDiskPagePath(VDiskSlotId, PDiskId, NodeId); - vdiskData.push({ - label: 'Links', - value: ( - - - - - ), - }); + const vDiskInternalViewerPath = valueIsDefined(VDiskSlotId) + ? createVDiskDeveloperUILink({ + nodeId: NodeId, + pDiskId: PDiskId, + vDiskSlotId: VDiskSlotId, + }) + : undefined; + + const vDiskPagePath = getVDiskLink({VDiskSlotId, PDiskId, NodeId, StringifiedId}); + if (vDiskPagePath) { + vdiskData.push({ + label: 'Links', + value: ( + + + {vDiskInternalViewerPath ? ( + + ) : null} + + ), + }); + } } return vdiskData; diff --git a/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx b/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx index 684a26ad43..0aa23e5ccf 100644 --- a/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx +++ b/src/containers/PDiskPage/PDiskSpaceDistribution/PDiskSpaceDistribution.tsx @@ -81,7 +81,7 @@ function Slot({item, pDiskId, nodeId}: SlotProps) { valueIsDefined(item.SlotData?.VDiskSlotId) && valueIsDefined(pDiskId) && valueIsDefined(nodeId) - ? getVDiskPagePath(item.SlotData.VDiskSlotId, pDiskId, nodeId) + ? getVDiskPagePath({vDiskSlotId: item.SlotData.VDiskSlotId, pDiskId, nodeId}) : undefined; return ( diff --git a/src/containers/VDiskPage/VDiskPage.tsx b/src/containers/VDiskPage/VDiskPage.tsx index 922e11be0e..6a4a6485a3 100644 --- a/src/containers/VDiskPage/VDiskPage.tsx +++ b/src/containers/VDiskPage/VDiskPage.tsx @@ -17,6 +17,7 @@ import {useDiskPagesAvailable} from '../../store/reducers/capabilities/hooks'; import {setHeaderBreadcrumbs} from '../../store/reducers/header/header'; import {vDiskApi} from '../../store/reducers/vdisk/vdisk'; import type {ModifyDiskResponse} from '../../types/api/modifyDisk'; +import type {TVDiskID} from '../../types/api/vdisk'; import {valueIsDefined} from '../../utils'; import {cn} from '../../utils/cn'; import {getSeverityColor, getVDiskSlotBasedId} from '../../utils/disks/helpers'; @@ -37,10 +38,11 @@ export function VDiskPage() { const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges(); const newDiskApiAvailable = useDiskPagesAvailable(); - const [{nodeId, pDiskId, vDiskSlotId}] = useQueryParams({ + const [{nodeId, pDiskId, vDiskSlotId, vDiskId: vDiskIdParam}] = useQueryParams({ nodeId: StringParam, pDiskId: StringParam, vDiskSlotId: StringParam, + vDiskId: StringParam, }); React.useEffect(() => { @@ -61,7 +63,9 @@ export function VDiskPage() { }); const loading = isFetching && vDiskData === undefined; const {NodeHost, NodeId, NodeType, NodeDC, PDiskId, PDiskType, Severity, VDiskId} = vDiskData; - const {GroupID, GroupGeneration, Ring, Domain, VDisk} = VDiskId || {}; + + const {GroupID, GroupGeneration, Ring, Domain, VDisk} = + VDiskId || getVDiskIdFromString(vDiskIdParam) || {}; const vDiskIdParamsDefined = valueIsDefined(GroupID) && valueIsDefined(GroupGeneration) && @@ -149,7 +153,7 @@ export function VDiskPage() { className={vDiskPageCn('title')} entityName={vDiskPageKeyset('vdisk')} status={getSeverityColor(Severity)} - id={vDiskData?.StringifiedId} + id={vDiskData?.StringifiedId ?? vDiskIdParam} /> ); }; @@ -228,3 +232,19 @@ export function VDiskPage() { ); } + +function getVDiskIdFromString(input: string | null | undefined): TVDiskID | undefined { + const match = /^(\d+)-(\d+)-(\d+)-(\d+)-(\d+)$/.exec(input ?? ''); + if (!match) { + return undefined; + } + + const [, GroupID, GroupGeneration, Ring, Domain, VDisk] = match; + return { + GroupID: Number(GroupID), + GroupGeneration: Number(GroupGeneration), + Ring: Number(Ring), + Domain: Number(Domain), + VDisk: Number(VDisk), + }; +} diff --git a/src/routes.ts b/src/routes.ts index 2dcee0832c..61e074a0ba 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -121,12 +121,16 @@ export function getPDiskPagePath( } export function getVDiskPagePath( - vDiskSlotId: string | number, - pDiskId: string | number, - nodeId: string | number, + params: + | { + vDiskSlotId: string | number; + pDiskId: string | number; + nodeId: string | number; + } + | {vDiskId: string; pDiskId?: string | number; nodeId?: string | number}, query: Query = {}, ) { - return createHref(routes.vDisk, undefined, {...query, nodeId, pDiskId, vDiskSlotId}); + return createHref(routes.vDisk, undefined, {...query, ...params}); } export function getStorageGroupPath(groupId: string | number, query: Query = {}) { diff --git a/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts b/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts index 02bf984ec7..a5bd6e75a5 100644 --- a/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts +++ b/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts @@ -359,6 +359,7 @@ describe('prepareGroupsPDisk', () => { const expectedResult = { NodeId: 224, + PDiskId: 1001, StringifiedId: '224-1001', Path: '/dev/disk/by-partlabel/kikimr_nvme_04', diff --git a/src/store/reducers/storage/prepareGroupsDisks.ts b/src/store/reducers/storage/prepareGroupsDisks.ts index 4b7cd53c3f..d49875a3e8 100644 --- a/src/store/reducers/storage/prepareGroupsDisks.ts +++ b/src/store/reducers/storage/prepareGroupsDisks.ts @@ -56,6 +56,13 @@ export function prepareGroupsPDisk(data: TStoragePDisk & {NodeId?: number} = {}) PDiskId: whiteboardPDisk?.PDiskId, }; + if (mergedPDiskData.PDiskId === undefined && bscPDisk.PDiskId) { + const id = Number(bscPDisk.PDiskId.split('-')[1]); + if (!isNaN(id)) { + mergedPDiskData.PDiskId = id; + } + } + const StringifiedId = bscPDisk.PDiskId || getPDiskId({nodeId: mergedPDiskData.NodeId, pDiskId: mergedPDiskData.PDiskId});