From 4dd51dc36e627973b945849ae5aafef1dc2d5885 Mon Sep 17 00:00:00 2001 From: Boris Yuzhakov Date: Thu, 27 Jun 2024 16:14:11 +0300 Subject: [PATCH 1/4] fix: rework overview pane in schema browser --- .gitignore | 3 + .../schemaOverview/CDCStreamOverview.tsx | 31 ---- .../schemaOverview/PersQueueGroupOverview.tsx | 38 ---- .../InfoViewer/schemaOverview/index.ts | 2 - .../Tenant/ObjectSummary/ObjectSummary.tsx | 173 +++++++++++------- 5 files changed, 114 insertions(+), 133 deletions(-) delete mode 100644 src/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx delete mode 100644 src/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx delete mode 100644 src/components/InfoViewer/schemaOverview/index.ts diff --git a/.gitignore b/.gitignore index 24767d979..4e6afaad9 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ yarn-debug.log* yarn-error.log* .env + + +embedded-ui.tar.bz2 \ No newline at end of file diff --git a/src/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx b/src/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx deleted file mode 100644 index 01202fcf0..000000000 --- a/src/components/InfoViewer/schemaOverview/CDCStreamOverview.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; - -import type {InfoViewerItem} from '..'; -import {InfoViewer, formatObject} from '..'; -import type {TEvDescribeSchemeResult} from '../../../types/api/schema'; -import {formatCdcStreamItem, formatCommonItem} from '../formatters'; - -interface CDCStreamOverviewProps { - data?: TEvDescribeSchemeResult; -} - -export const CDCStreamOverview = ({data}: CDCStreamOverviewProps) => { - if (!data) { - return
No CDC Stream data
; - } - - const TableIndex = data.PathDescription?.CdcStreamDescription; - const info: Array = []; - - info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType)); - info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep)); - - const {Mode, Format} = TableIndex || {}; - info.push(...formatObject(formatCdcStreamItem, {Mode, Format})); - - return ( - - {info.length ? : 'Empty'} - - ); -}; diff --git a/src/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx b/src/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx deleted file mode 100644 index a18939afe..000000000 --- a/src/components/InfoViewer/schemaOverview/PersQueueGroupOverview.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; - -import type {InfoViewerItem} from '..'; -import {InfoViewer} from '..'; -import type {TEvDescribeSchemeResult} from '../../../types/api/schema'; -import {formatCommonItem, formatPQGroupItem} from '../formatters'; - -interface PersQueueGroupOverviewProps { - data?: TEvDescribeSchemeResult; -} - -export const PersQueueGroupOverview = ({data}: PersQueueGroupOverviewProps) => { - if (!data) { - return
No PersQueueGroup data
; - } - - const pqGroup = data.PathDescription?.PersQueueGroup; - const info: Array = []; - - info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType)); - info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep)); - - //@ts-expect-error - info.push(formatPQGroupItem('Partitions', pqGroup?.Partitions || [])); - info.push( - //@ts-expect-error - formatPQGroupItem( - 'PQTabletConfig', - pqGroup?.PQTabletConfig || {PartitionConfig: {LifetimeSeconds: 0}}, - ), - ); - - return ( - - {info.length ? : 'Empty'} - - ); -}; diff --git a/src/components/InfoViewer/schemaOverview/index.ts b/src/components/InfoViewer/schemaOverview/index.ts deleted file mode 100644 index 5da37b7dd..000000000 --- a/src/components/InfoViewer/schemaOverview/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './CDCStreamOverview'; -export * from './PersQueueGroupOverview'; diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index 91b02a4c6..868a3618a 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {HelpPopover} from '@gravity-ui/components'; +import {dateTimeParse} from '@gravity-ui/date-utils'; import {LayoutHeaderCellsLargeFill} from '@gravity-ui/icons'; import {Button, Icon, Tabs} from '@gravity-ui/uikit'; import qs from 'qs'; @@ -10,12 +11,10 @@ import {Link} from 'react-router-dom'; import {AsyncReplicationState} from '../../../components/AsyncReplicationState'; import {ClipboardButton} from '../../../components/ClipboardButton'; import InfoViewer from '../../../components/InfoViewer/InfoViewer'; -import { - CDCStreamOverview, - PersQueueGroupOverview, -} from '../../../components/InfoViewer/schemaOverview'; +import type {InfoViewerItem} from '../../../components/InfoViewer/InfoViewer'; import {Loader} from '../../../components/Loader'; import SplitPane from '../../../components/SplitPane'; +import {getEntityName} from '../../../containers/Tenant/utils'; import routes, {createHref} from '../../../routes'; import {setShowPreview} from '../../../store/reducers/schema/schema'; import { @@ -24,15 +23,16 @@ import { TENANT_SUMMARY_TABS_IDS, } from '../../../store/reducers/tenant/constants'; import {setQueryTab, setSummaryTab, setTenantPage} from '../../../store/reducers/tenant/tenant'; -import type {EPathSubType} from '../../../types/api/schema'; -import {EPathType} from '../../../types/api/schema'; +import {EPathSubType, EPathType} from '../../../types/api/schema'; import {cn} from '../../../utils/cn'; import { DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED, DEFAULT_SIZE_TENANT_SUMMARY_KEY, + HOUR_IN_SECONDS, } from '../../../utils/constants'; -import {formatDateTime} from '../../../utils/dataFormatters/dataFormatters'; +import {formatNumber} from '../../../utils/dataFormatters/dataFormatters'; import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks'; +import {isNumeric} from '../../../utils/utils'; import {Acl} from '../Acl/Acl'; import {ExternalDataSourceSummary} from '../Info/ExternalDataSource/ExternalDataSource'; import {ExternalTableSummary} from '../Info/ExternalTable/ExternalTable'; @@ -140,65 +140,114 @@ export function ObjectSummary({ }; const renderObjectOverview = () => { - const startTimeInMilliseconds = Number(currentSchemaData?.CreateStep); - const createdAt = startTimeInMilliseconds - ? formatDateTime(startTimeInMilliseconds) - : 'unknown'; - const createdAtLabel = 'Created At'; - - // verbose mapping to guarantee a correct render for new path types - // TS will error when a new type is added but not mapped here - const pathTypeToComponent: Record React.ReactNode) | undefined> = { - [EPathType.EPathTypeInvalid]: undefined, - [EPathType.EPathTypeDir]: undefined, - [EPathType.EPathTypeTable]: undefined, - [EPathType.EPathTypeSubDomain]: undefined, - [EPathType.EPathTypeTableIndex]: undefined, - [EPathType.EPathTypeExtSubDomain]: undefined, - [EPathType.EPathTypeColumnStore]: undefined, - [EPathType.EPathTypeColumnTable]: undefined, - [EPathType.EPathTypeCdcStream]: () => , - [EPathType.EPathTypePersQueueGroup]: () => ( - - ), - [EPathType.EPathTypeExternalTable]: () => ( - - ), - [EPathType.EPathTypeExternalDataSource]: () => ( - - ), - [EPathType.EPathTypeView]: undefined, - [EPathType.EPathTypeReplication]: () => ( - - ), - }, - ]} - /> - ), - }; + if (!currentSchemaData) return undefined; + const {CreateStep, PathType, PathSubType, PathId, PathVersion} = currentSchemaData; + + const overview: InfoViewerItem[] = []; + + // for any schema type + overview.push({label: 'Type', value: PathType?.replace(/^EPathType/, '')}); - let component = - currentSchemaData?.PathType && pathTypeToComponent[currentSchemaData.PathType]?.(); + // show SubType if it's not EPathSubTypeEmpty + if (PathSubType !== EPathSubType.EPathSubTypeEmpty) { + overview.push({label: 'SubType', value: PathSubType?.replace(/^EPathSubType/, '')}); + } + + // show PathId + // if you get 18446744073709551615 = ffffffff = 2n ** 64n - 1n =-1 в ui64 + // you need to ask @xeno0904 for help + overview.push({label: 'Id', value: PathId}); - if (!component) { - component = ; + // show PathVersion + overview.push({label: 'Version', value: PathVersion}); + + // show created time if it's not 0 + if (isNumeric(CreateStep) && Number(CreateStep)) { + overview.push({ + label: 'Created', + value: dateTimeParse(Number(CreateStep))?.format('YYYY-MM-DD HH:mm'), + }); + } + + const {PathDescription} = currentObjectData; + const title = getEntityName(PathDescription); + + // Table: show Partitions count = len(TablePartitions) + if (PathType === EPathType.EPathTypeTable) { + overview.push({ + label: 'Partitions count', + value: PathDescription?.TablePartitions?.length, + }); + } + + // ColumnTable: show Partitions count = len(ColumnTableDescription.Sharding.ColumnShards) + if (PathType === EPathType.EPathTypeColumnTable) { + overview.push({ + label: 'Partitions count', + value: PathDescription?.ColumnTableDescription?.Sharding?.ColumnShards?.length, + }); + } + + // ColumnStore: show Partitions count = len(ColumnStoreDescription.ColumnShards) + if (PathType === EPathType.EPathTypeColumnStore) { + overview.push({ + label: 'Partitions count', + value: PathDescription?.ColumnStoreDescription?.ColumnShards?.length, + }); + } + + // ExtSubDomain: (database) + // show Paths (DomainDescription.PathsInside) + // show Shards (DomainDescription.ShardsInside) + if (PathType === EPathType.EPathTypeExtSubDomain) { + overview.push({ + label: 'Paths count', + value: PathDescription?.DomainDescription?.PathsInside?.length, + }); + overview.push({ + label: 'Shards count', + value: PathDescription?.DomainDescription?.ShardsInside?.length, + }); + } + + if (PathType === EPathType.EPathTypeReplication) { + const state = PathDescription?.ReplicationDescription?.State; + if (state) { + overview.push({label: 'State', value: }); + } + } + + if (PathType === EPathType.EPathTypeCdcStream) { + const {Mode, Format} = PathDescription?.CdcStreamDescription || {}; + + overview.push({label: 'Mode', value: Mode?.replace(/^ECdcStreamMode/, '')}); + overview.push({ + label: 'Format', + value: Format?.replace(/^ECdcStreamFormat/, ''), + }); + } + if (PathType === EPathType.EPathTypePersQueueGroup) { + const pqGroup = PathDescription?.PersQueueGroup; + + overview.push({label: 'Partitions count', value: pqGroup?.Partitions?.length}); + + const value = pqGroup?.PQTabletConfig?.PartitionConfig?.LifetimeSeconds; + if (value) { + const hours = (value / HOUR_IN_SECONDS).toFixed(2); + overview.push({label: 'Retention', value: `${formatNumber(hours)} hours`}); + } + } + + if (PathType === EPathType.EPathTypeExternalTable) { + return ; + } + if (PathType === EPathType.EPathTypeExternalDataSource) { + return ; } - return component; + // filter all empty values in according this requirement + // https://github.com/ydb-platform/ydb-embedded-ui/issues/906 + return i.value)} />; }; const renderLoader = () => { From e36490aaa0f12c13d4466aaa4993733d1832c126 Mon Sep 17 00:00:00 2001 From: Boris Yuzhakov Date: Thu, 27 Jun 2024 23:38:37 +0300 Subject: [PATCH 2/4] fix: in according review comments --- .eslintrc | 21 +++++- .../Tenant/ObjectSummary/ObjectSummary.tsx | 75 +++++++++---------- src/containers/Tenant/i18n/en.json | 17 +++-- src/utils/dataFormatters/dataFormatters.ts | 7 +- 4 files changed, 69 insertions(+), 51 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5f81ea860..9dc0df0ea 100644 --- a/.eslintrc +++ b/.eslintrc @@ -7,7 +7,10 @@ "root": true, "overrides": [ { - "files": ["config-overrides.js", "commitlint.config.js"], + "files": [ + "config-overrides.js", + "commitlint.config.js" + ], "env": { "node": true, }, @@ -17,10 +20,20 @@ "project": "./tsconfig.json", }, "rules": { - "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + "import/consistent-type-specifier-style": [ + "error", + "prefer-top-level" + ], "@typescript-eslint/consistent-type-imports": [ "error", - {"prefer": "type-imports", "fixStyle": "separate-type-imports"}, + { + "prefer": "type-imports", + "fixStyle": "separate-type-imports" + }, + ], + "curly": [ + "error", + "all" ], }, -} +} \ No newline at end of file diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index 8b66309fe..f987927c7 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -1,7 +1,6 @@ import React from 'react'; import {HelpPopover} from '@gravity-ui/components'; -import {dateTimeParse} from '@gravity-ui/date-utils'; import {LayoutHeaderCellsLargeFill} from '@gravity-ui/icons'; import {Button, Icon, Tabs} from '@gravity-ui/uikit'; import qs from 'qs'; @@ -29,11 +28,9 @@ import {cn} from '../../../utils/cn'; import { DEFAULT_IS_TENANT_COMMON_INFO_COLLAPSED, DEFAULT_SIZE_TENANT_SUMMARY_KEY, - HOUR_IN_SECONDS, } from '../../../utils/constants'; -import {formatNumber} from '../../../utils/dataFormatters/dataFormatters'; +import {formatDateTime, formatSecondsToHours} from '../../../utils/dataFormatters/dataFormatters'; import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks'; -import {isNumeric} from '../../../utils/utils'; import {Acl} from '../Acl/Acl'; import {ExternalDataSourceSummary} from '../Info/ExternalDataSource/ExternalDataSource'; import {ExternalTableSummary} from '../Info/ExternalTable/ExternalTable'; @@ -137,72 +134,62 @@ export function ObjectSummary({ }; const renderObjectOverview = () => { - if (!currentSchemaData) return undefined; + if (!currentSchemaData) { + return undefined; + } const {CreateStep, PathType, PathSubType, PathId, PathVersion} = currentSchemaData; const overview: InfoViewerItem[] = []; - // for any schema type - overview.push({label: 'Type', value: PathType?.replace(/^EPathType/, '')}); + overview.push({label: i18n('summary.type'), value: PathType?.replace(/^EPathType/, '')}); - // show SubType if it's not EPathSubTypeEmpty if (PathSubType !== EPathSubType.EPathSubTypeEmpty) { - overview.push({label: 'SubType', value: PathSubType?.replace(/^EPathSubType/, '')}); + overview.push({ + label: i18n('summary.subtype'), + value: PathSubType?.replace(/^EPathSubType/, ''), + }); } - // show PathId - // if you get 18446744073709551615 = ffffffff = 2n ** 64n - 1n =-1 в ui64 - // you need to ask @xeno0904 for help - overview.push({label: 'Id', value: PathId}); + overview.push({label: i18n('summary.id'), value: PathId}); - // show PathVersion - overview.push({label: 'Version', value: PathVersion}); + overview.push({label: i18n('summary.version'), value: PathVersion}); - // show created time if it's not 0 - if (isNumeric(CreateStep) && Number(CreateStep)) { - overview.push({ - label: 'Created', - value: dateTimeParse(Number(CreateStep))?.format('YYYY-MM-DD HH:mm'), - }); - } + overview.push({ + label: i18n('summary.created'), + value: formatDateTime(CreateStep), + }); const {PathDescription} = currentObjectData; const title = getEntityName(PathDescription); - // Table: show Partitions count = len(TablePartitions) if (PathType === EPathType.EPathTypeTable) { overview.push({ - label: 'Partitions count', + label: i18n('summary.partitions'), value: PathDescription?.TablePartitions?.length, }); } - // ColumnTable: show Partitions count = len(ColumnTableDescription.Sharding.ColumnShards) if (PathType === EPathType.EPathTypeColumnTable) { overview.push({ - label: 'Partitions count', + label: i18n('summary.partitions'), value: PathDescription?.ColumnTableDescription?.Sharding?.ColumnShards?.length, }); } - // ColumnStore: show Partitions count = len(ColumnStoreDescription.ColumnShards) if (PathType === EPathType.EPathTypeColumnStore) { overview.push({ - label: 'Partitions count', + label: i18n('summary.partitions'), value: PathDescription?.ColumnStoreDescription?.ColumnShards?.length, }); } - // ExtSubDomain: (database) - // show Paths (DomainDescription.PathsInside) - // show Shards (DomainDescription.ShardsInside) if (PathType === EPathType.EPathTypeExtSubDomain) { overview.push({ - label: 'Paths count', + label: i18n('summary.paths'), value: PathDescription?.DomainDescription?.PathsInside?.length, }); overview.push({ - label: 'Shards count', + label: i18n('summary.shards'), value: PathDescription?.DomainDescription?.ShardsInside?.length, }); } @@ -210,29 +197,35 @@ export function ObjectSummary({ if (PathType === EPathType.EPathTypeReplication) { const state = PathDescription?.ReplicationDescription?.State; if (state) { - overview.push({label: 'State', value: }); + overview.push({ + label: i18n('summary.state'), + value: , + }); } } if (PathType === EPathType.EPathTypeCdcStream) { const {Mode, Format} = PathDescription?.CdcStreamDescription || {}; - overview.push({label: 'Mode', value: Mode?.replace(/^ECdcStreamMode/, '')}); overview.push({ - label: 'Format', + label: i18n('summary.mode'), + value: Mode?.replace(/^ECdcStreamMode/, ''), + }); + overview.push({ + label: i18n('summary.format'), value: Format?.replace(/^ECdcStreamFormat/, ''), }); } if (PathType === EPathType.EPathTypePersQueueGroup) { const pqGroup = PathDescription?.PersQueueGroup; - overview.push({label: 'Partitions count', value: pqGroup?.Partitions?.length}); + overview.push({label: i18n('summary.partitions'), value: pqGroup?.Partitions?.length}); const value = pqGroup?.PQTabletConfig?.PartitionConfig?.LifetimeSeconds; - if (value) { - const hours = (value / HOUR_IN_SECONDS).toFixed(2); - overview.push({label: 'Retention', value: `${formatNumber(hours)} hours`}); - } + overview.push({ + label: i18n('summary.retention'), + value: value && formatSecondsToHours(value), + }); } if (PathType === EPathType.EPathTypeExternalTable) { diff --git a/src/containers/Tenant/i18n/en.json b/src/containers/Tenant/i18n/en.json index 49d2366ca..16031a6af 100644 --- a/src/containers/Tenant/i18n/en.json +++ b/src/containers/Tenant/i18n/en.json @@ -1,19 +1,26 @@ { "page.title": "Database", - "pages.query": "Query", "pages.diagnostics": "Diagnostics", - "acl.owner": "Owner", "acl.empty": "No Acl data", - "summary.navigation": "Navigation", "summary.showPreview": "Show preview", "summary.copySchemaPath": "Copy schema path", - + "summary.type": "Type", + "summary.subtype": "SubType", + "summary.id": "Id", + "summary.version": "Version", + "summary.created": "Created", + "summary.partitions": "Partitions count", + "summary.paths": "Paths count", + "summary.shards": "Shards count", + "summary.state": "State", + "summary.mode": "Mode", + "summary.format": "Format", + "summary.retention": "Retention", "actions.copied": "The path is copied to the clipboard", "actions.notCopied": "Couldn’t copy the path", - "actions.copyPath": "Copy path", "actions.openPreview": "Open preview", "actions.createTable": "Create table...", diff --git a/src/utils/dataFormatters/dataFormatters.ts b/src/utils/dataFormatters/dataFormatters.ts index 063506ce7..f8b0ff038 100644 --- a/src/utils/dataFormatters/dataFormatters.ts +++ b/src/utils/dataFormatters/dataFormatters.ts @@ -6,7 +6,7 @@ import { getSizeWithSignificantDigits, } from '../bytesParsers/formatBytes'; import type {BytesSizes} from '../bytesParsers/formatBytes'; -import {DAY_IN_SECONDS, GIGABYTE} from '../constants'; +import {DAY_IN_SECONDS, GIGABYTE, HOUR_IN_SECONDS} from '../constants'; import {configuredNumeral} from '../numeral'; import {isNumeric} from '../utils'; @@ -99,6 +99,11 @@ export const formatNumber = (number?: unknown) => { return configuredNumeral(number).format('0,0.[00000]'); }; +export const formatSecondsToHours = (seconds: number) => { + const hours = (seconds / HOUR_IN_SECONDS).toFixed(2); + return `${formatNumber(hours)} hours`; +}; + export const roundToPrecision = (value: number | string, precision = 0) => { let [digits] = String(value).split('.'); if (Number(value) < 1) { From f2a65900bc9ee3b61b37a2b9080f40c52cc5dcdf Mon Sep 17 00:00:00 2001 From: Boris Yuzhakov Date: Fri, 28 Jun 2024 16:09:31 +0300 Subject: [PATCH 3/4] fix: add ts checking for overview info --- .../Tenant/ObjectSummary/ObjectSummary.tsx | 185 +++++++++++------- src/containers/Tenant/i18n/en.json | 2 + src/utils/dataFormatters/dataFormatters.ts | 4 +- 3 files changed, 115 insertions(+), 76 deletions(-) diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index f987927c7..b2e1d06a2 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -12,10 +12,11 @@ import {AsyncReplicationState} from '../../../components/AsyncReplicationState'; import {ClipboardButton} from '../../../components/ClipboardButton'; import InfoViewer from '../../../components/InfoViewer/InfoViewer'; import type {InfoViewerItem} from '../../../components/InfoViewer/InfoViewer'; +import {LinkWithIcon} from '../../../components/LinkWithIcon/LinkWithIcon'; import {Loader} from '../../../components/Loader'; import SplitPane from '../../../components/SplitPane'; import {getEntityName} from '../../../containers/Tenant/utils'; -import routes, {createHref} from '../../../routes'; +import routes, {createExternalUILink, createHref} from '../../../routes'; import {schemaApi, setShowPreview} from '../../../store/reducers/schema/schema'; import { TENANT_PAGES_IDS, @@ -32,8 +33,6 @@ import { import {formatDateTime, formatSecondsToHours} from '../../../utils/dataFormatters/dataFormatters'; import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks'; import {Acl} from '../Acl/Acl'; -import {ExternalDataSourceSummary} from '../Info/ExternalDataSource/ExternalDataSource'; -import {ExternalTableSummary} from '../Info/ExternalTable/ExternalTable'; import {SchemaTree} from '../Schema/SchemaTree/SchemaTree'; import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer'; import {TENANT_INFO_TABS, TENANT_SCHEMA_TAB, TenantTabsGroups} from '../TenantPages'; @@ -156,84 +155,122 @@ export function ObjectSummary({ overview.push({ label: i18n('summary.created'), - value: formatDateTime(CreateStep), + value: formatDateTime(CreateStep, ''), }); const {PathDescription} = currentObjectData; const title = getEntityName(PathDescription); - if (PathType === EPathType.EPathTypeTable) { - overview.push({ - label: i18n('summary.partitions'), - value: PathDescription?.TablePartitions?.length, - }); - } - - if (PathType === EPathType.EPathTypeColumnTable) { - overview.push({ - label: i18n('summary.partitions'), - value: PathDescription?.ColumnTableDescription?.Sharding?.ColumnShards?.length, - }); - } - - if (PathType === EPathType.EPathTypeColumnStore) { - overview.push({ - label: i18n('summary.partitions'), - value: PathDescription?.ColumnStoreDescription?.ColumnShards?.length, - }); - } - - if (PathType === EPathType.EPathTypeExtSubDomain) { - overview.push({ - label: i18n('summary.paths'), - value: PathDescription?.DomainDescription?.PathsInside?.length, - }); - overview.push({ - label: i18n('summary.shards'), - value: PathDescription?.DomainDescription?.ShardsInside?.length, - }); - } - - if (PathType === EPathType.EPathTypeReplication) { - const state = PathDescription?.ReplicationDescription?.State; - if (state) { - overview.push({ - label: i18n('summary.state'), - value: , + const getPathTypeOverview: Record InfoViewerItem[]) | undefined> = { + [EPathType.EPathTypeInvalid]: undefined, + [EPathType.EPathTypeDir]: undefined, + [EPathType.EPathTypeTable]: () => [ + { + label: i18n('summary.partitions'), + value: PathDescription?.TablePartitions?.length, + }, + ], + [EPathType.EPathTypeSubDomain]: undefined, + [EPathType.EPathTypeTableIndex]: undefined, + [EPathType.EPathTypeExtSubDomain]: () => [ + { + label: i18n('summary.paths'), + value: PathDescription?.DomainDescription?.PathsInside?.length, + }, + { + label: i18n('summary.shards'), + value: PathDescription?.DomainDescription?.ShardsInside?.length, + }, + ], + [EPathType.EPathTypeColumnStore]: () => [ + { + label: i18n('summary.partitions'), + value: PathDescription?.ColumnStoreDescription?.ColumnShards?.length, + }, + ], + [EPathType.EPathTypeColumnTable]: () => [ + { + label: i18n('summary.partitions'), + value: PathDescription?.ColumnTableDescription?.Sharding?.ColumnShards?.length, + }, + ], + [EPathType.EPathTypeCdcStream]: () => { + const {Mode, Format} = PathDescription?.CdcStreamDescription || {}; + + return [ + { + label: i18n('summary.mode'), + value: Mode?.replace(/^ECdcStreamMode/, ''), + }, + { + label: i18n('summary.format'), + value: Format?.replace(/^ECdcStreamFormat/, ''), + }, + ]; + }, + [EPathType.EPathTypePersQueueGroup]: () => { + const pqGroup = PathDescription?.PersQueueGroup; + const value = pqGroup?.PQTabletConfig?.PartitionConfig?.LifetimeSeconds; + + return [ + { + label: i18n('summary.partitions'), + value: pqGroup?.Partitions?.length, + }, + { + label: i18n('summary.retention'), + value: value && formatSecondsToHours(value), + }, + ]; + }, + [EPathType.EPathTypeExternalTable]: () => { + const pathToDataSource = createExternalUILink({ + ...queryParams, + schema: PathDescription?.ExternalTableDescription?.DataSourcePath, }); - } - } - if (PathType === EPathType.EPathTypeCdcStream) { - const {Mode, Format} = PathDescription?.CdcStreamDescription || {}; - - overview.push({ - label: i18n('summary.mode'), - value: Mode?.replace(/^ECdcStreamMode/, ''), - }); - overview.push({ - label: i18n('summary.format'), - value: Format?.replace(/^ECdcStreamFormat/, ''), - }); - } - if (PathType === EPathType.EPathTypePersQueueGroup) { - const pqGroup = PathDescription?.PersQueueGroup; - - overview.push({label: i18n('summary.partitions'), value: pqGroup?.Partitions?.length}); - - const value = pqGroup?.PQTabletConfig?.PartitionConfig?.LifetimeSeconds; - overview.push({ - label: i18n('summary.retention'), - value: value && formatSecondsToHours(value), - }); - } - - if (PathType === EPathType.EPathTypeExternalTable) { - return ; - } - if (PathType === EPathType.EPathTypeExternalDataSource) { - return ; - } + const {SourceType, DataSourcePath} = + PathDescription?.ExternalTableDescription || {}; + + const dataSourceName = DataSourcePath?.match(/([^/]*)\/*$/)?.[1] || ''; + + return [ + {label: i18n('summary.source-type'), value: SourceType}, + { + label: i18n('summary.data-source'), + value: DataSourcePath && ( + + + + ), + }, + ]; + }, + [EPathType.EPathTypeExternalDataSource]: () => [ + { + label: i18n('summary.source-type'), + value: PathDescription?.ExternalDataSourceDescription?.SourceType, + }, + ], + [EPathType.EPathTypeView]: undefined, + [EPathType.EPathTypeReplication]: () => { + const state = PathDescription?.ReplicationDescription?.State; + + if (!state) { + return []; + } + + return [ + { + label: i18n('summary.state'), + value: , + }, + ]; + }, + }; + + const pathTypeOverview = (PathType && getPathTypeOverview[PathType]?.()) || []; + overview.push(...pathTypeOverview); // filter all empty values in according this requirement // https://github.com/ydb-platform/ydb-embedded-ui/issues/906 diff --git a/src/containers/Tenant/i18n/en.json b/src/containers/Tenant/i18n/en.json index 16031a6af..659973318 100644 --- a/src/containers/Tenant/i18n/en.json +++ b/src/containers/Tenant/i18n/en.json @@ -6,6 +6,8 @@ "acl.empty": "No Acl data", "summary.navigation": "Navigation", "summary.showPreview": "Show preview", + "summary.source-type": "Source Type", + "summary.data-source": "Data Source", "summary.copySchemaPath": "Copy schema path", "summary.type": "Type", "summary.subtype": "SubType", diff --git a/src/utils/dataFormatters/dataFormatters.ts b/src/utils/dataFormatters/dataFormatters.ts index f8b0ff038..212bd8563 100644 --- a/src/utils/dataFormatters/dataFormatters.ts +++ b/src/utils/dataFormatters/dataFormatters.ts @@ -139,14 +139,14 @@ export const formatCPUWithLabel = (value?: number) => { return `${localizedCores} ${i18n('format-cpu.cores', {count: cores})}`; }; -export const formatDateTime = (value?: number | string) => { +export const formatDateTime = (value?: number | string, defaultValue = 'N/A') => { if (!isNumeric(value)) { return ''; } const formattedData = dateTimeParse(Number(value))?.format('YYYY-MM-DD HH:mm'); - return Number(value) > 0 && formattedData ? formattedData : 'N/A'; + return Number(value) > 0 && formattedData ? formattedData : defaultValue; }; export const calcUptimeInSeconds = (milliseconds: number | string) => { From 44a8e59e5572ff47524efaed1b72678e6733a62f Mon Sep 17 00:00:00 2001 From: Boris Yuzhakov Date: Fri, 28 Jun 2024 17:15:53 +0300 Subject: [PATCH 4/4] fix: paths and shards view - remove length --- src/containers/Tenant/ObjectSummary/ObjectSummary.tsx | 4 ++-- src/containers/Tenant/i18n/en.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index b2e1d06a2..ca7c84d57 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -175,11 +175,11 @@ export function ObjectSummary({ [EPathType.EPathTypeExtSubDomain]: () => [ { label: i18n('summary.paths'), - value: PathDescription?.DomainDescription?.PathsInside?.length, + value: PathDescription?.DomainDescription?.PathsInside, }, { label: i18n('summary.shards'), - value: PathDescription?.DomainDescription?.ShardsInside?.length, + value: PathDescription?.DomainDescription?.ShardsInside, }, ], [EPathType.EPathTypeColumnStore]: () => [ diff --git a/src/containers/Tenant/i18n/en.json b/src/containers/Tenant/i18n/en.json index 939d94a05..defb9ee44 100644 --- a/src/containers/Tenant/i18n/en.json +++ b/src/containers/Tenant/i18n/en.json @@ -15,8 +15,8 @@ "summary.version": "Version", "summary.created": "Created", "summary.partitions": "Partitions count", - "summary.paths": "Paths count", - "summary.shards": "Shards count", + "summary.paths": "Paths", + "summary.shards": "Shards", "summary.state": "State", "summary.mode": "Mode", "summary.format": "Format",