Skip to content

Commit a390679

Browse files
feat: add ShardsTable to componentsRegistry (#1993)
1 parent a544def commit a390679

File tree

15 files changed

+228
-174
lines changed

15 files changed

+228
-174
lines changed

src/components/ComponentsProvider/componentsRegistry.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {AsideNavigation} from '../../containers/AsideNavigation/AsideNavigation';
22
import {ErrorBoundaryInner} from '../ErrorBoundary/ErrorBoundary';
3+
import {ShardsTable} from '../ShardsTable/ShardsTable';
34
import {StaffCard} from '../User/StaffCard';
45

56
import type {ComponentsRegistryTemplate} from './registry';
@@ -8,7 +9,8 @@ import {Registry} from './registry';
89
const componentsRegistryInner = new Registry()
910
.register('StaffCard', StaffCard)
1011
.register('AsideNavigation', AsideNavigation)
11-
.register('ErrorBoundary', ErrorBoundaryInner);
12+
.register('ErrorBoundary', ErrorBoundaryInner)
13+
.register('ShardsTable', ShardsTable);
1214

1315
export type ComponentsRegistry = ComponentsRegistryTemplate<typeof componentsRegistryInner>;
1416

src/components/LinkToSchemaObject/LinkToSchemaObject.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import {Link} from '@gravity-ui/uikit';
22
import type {LinkProps} from '@gravity-ui/uikit';
3-
import type {Location} from 'history';
3+
import {useLocation} from 'react-router-dom';
44

55
import {createExternalUILink, parseQuery} from '../../routes';
66

77
interface LinkToSchemaObjectProps extends Omit<LinkProps, 'href'> {
88
path: string;
9-
location: Location;
109
}
1110

12-
export function LinkToSchemaObject({path, location, ...props}: LinkToSchemaObjectProps) {
11+
export function LinkToSchemaObject({path, ...props}: LinkToSchemaObjectProps) {
12+
const location = useLocation();
1313
const queryParams = parseQuery(location);
1414
const pathToSchemaObject = createExternalUILink({
1515
...queryParams,
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
3+
import type {KeyValueRow} from '../../types/api/query';
4+
import type {ResizeableDataTableProps} from '../ResizeableDataTable/ResizeableDataTable';
5+
import {ResizeableDataTable} from '../ResizeableDataTable/ResizeableDataTable';
6+
7+
import {shardsColumnIdToGetColumn} from './columns';
8+
import type {TopShardsColumnId} from './constants';
9+
import {TOP_SHARDS_COLUMNS_WIDTH_LS_KEY, isSortableTopShardsColumn} from './constants';
10+
11+
export interface ShardsTableProps
12+
extends Omit<ResizeableDataTableProps<KeyValueRow>, 'columnsWidthLSKey' | 'columns'> {
13+
columnsIds: TopShardsColumnId[];
14+
database: string;
15+
schemaPath?: string;
16+
}
17+
18+
export function ShardsTable({columnsIds, schemaPath, database, ...props}: ShardsTableProps) {
19+
const columns = React.useMemo(
20+
() =>
21+
columnsIds
22+
.filter((id) => id in shardsColumnIdToGetColumn)
23+
.map((id) => {
24+
const column = shardsColumnIdToGetColumn[id]({database, schemaPath});
25+
26+
return {
27+
...column,
28+
sortable: isSortableTopShardsColumn(column.name),
29+
};
30+
}),
31+
[columnsIds, database, schemaPath],
32+
);
33+
34+
return (
35+
<ResizeableDataTable
36+
{...props}
37+
columnsWidthLSKey={TOP_SHARDS_COLUMNS_WIDTH_LS_KEY}
38+
columns={columns}
39+
/>
40+
);
41+
}
+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import DataTable from '@gravity-ui/react-data-table';
2+
3+
import {getDefaultNodePath} from '../../containers/Node/NodePages';
4+
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
5+
import {formatNumber, roundToPrecision} from '../../utils/dataFormatters/dataFormatters';
6+
import {getUsageSeverity} from '../../utils/generateEvaluator';
7+
import {InternalLink} from '../InternalLink';
8+
import {LinkToSchemaObject} from '../LinkToSchemaObject/LinkToSchemaObject';
9+
import {TabletNameWrapper} from '../TabletNameWrapper/TabletNameWrapper';
10+
import {UsageLabel} from '../UsageLabel/UsageLabel';
11+
12+
import type {TopShardsColumnId} from './constants';
13+
import {TOP_SHARDS_COLUMNS_IDS, TOP_SHARDS_COLUMNS_TITLES} from './constants';
14+
import type {GetShardsColumn} from './types';
15+
import {prepareDateTimeValue} from './utils';
16+
17+
export const getPathColumn: GetShardsColumn = ({schemaPath = ''}) => {
18+
return {
19+
name: TOP_SHARDS_COLUMNS_IDS.Path,
20+
header: TOP_SHARDS_COLUMNS_TITLES.Path,
21+
render: ({row}) => {
22+
// row.Path - relative schema path
23+
return <LinkToSchemaObject path={schemaPath + row.Path}>{row.Path}</LinkToSchemaObject>;
24+
},
25+
width: 300,
26+
};
27+
};
28+
export const getDataSizeColumn: GetShardsColumn = () => {
29+
return {
30+
name: TOP_SHARDS_COLUMNS_IDS.DataSize,
31+
header: TOP_SHARDS_COLUMNS_TITLES.DataSize,
32+
render: ({row}) => {
33+
return formatNumber(row.DataSize);
34+
},
35+
align: DataTable.RIGHT,
36+
};
37+
};
38+
export const getTabletIdColumn: GetShardsColumn = () => {
39+
return {
40+
name: TOP_SHARDS_COLUMNS_IDS.TabletId,
41+
header: TOP_SHARDS_COLUMNS_TITLES.TabletId,
42+
render: ({row}) => {
43+
if (!row.TabletId) {
44+
return EMPTY_DATA_PLACEHOLDER;
45+
}
46+
return <TabletNameWrapper tabletId={row.TabletId} />;
47+
},
48+
width: 220,
49+
};
50+
};
51+
export const getNodeIdColumn: GetShardsColumn = () => {
52+
return {
53+
name: TOP_SHARDS_COLUMNS_IDS.NodeId,
54+
header: TOP_SHARDS_COLUMNS_TITLES.NodeId,
55+
render: ({row}) => {
56+
if (!row.NodeId) {
57+
return EMPTY_DATA_PLACEHOLDER;
58+
}
59+
return <InternalLink to={getDefaultNodePath(row.NodeId)}>{row.NodeId}</InternalLink>;
60+
},
61+
align: DataTable.RIGHT,
62+
};
63+
};
64+
export const getCpuCoresColumn: GetShardsColumn = () => {
65+
return {
66+
name: TOP_SHARDS_COLUMNS_IDS.CPUCores,
67+
header: TOP_SHARDS_COLUMNS_TITLES.CPUCores,
68+
render: ({row}) => {
69+
const usage = Number(row.CPUCores) * 100 || 0;
70+
return (
71+
<UsageLabel value={roundToPrecision(usage, 2)} theme={getUsageSeverity(usage)} />
72+
);
73+
},
74+
align: DataTable.RIGHT,
75+
width: 110,
76+
resizeMinWidth: 110,
77+
};
78+
};
79+
export const getInFlightTxCountColumn: GetShardsColumn = () => {
80+
return {
81+
name: TOP_SHARDS_COLUMNS_IDS.InFlightTxCount,
82+
header: TOP_SHARDS_COLUMNS_TITLES.InFlightTxCount,
83+
render: ({row}) => formatNumber(row.InFlightTxCount),
84+
align: DataTable.RIGHT,
85+
};
86+
};
87+
export const getPeakTimeColumn: GetShardsColumn = () => {
88+
return {
89+
name: TOP_SHARDS_COLUMNS_IDS.PeakTime,
90+
render: ({row}) => {
91+
return prepareDateTimeValue(row.PeakTime);
92+
},
93+
};
94+
};
95+
export const getIntervalEndColumn: GetShardsColumn = () => {
96+
return {
97+
name: TOP_SHARDS_COLUMNS_IDS.IntervalEnd,
98+
render: ({row}) => {
99+
return prepareDateTimeValue(row.IntervalEnd);
100+
},
101+
};
102+
};
103+
104+
export const shardsColumnIdToGetColumn: Record<TopShardsColumnId, GetShardsColumn> = {
105+
[TOP_SHARDS_COLUMNS_IDS.Path]: getPathColumn,
106+
[TOP_SHARDS_COLUMNS_IDS.DataSize]: getDataSizeColumn,
107+
[TOP_SHARDS_COLUMNS_IDS.TabletId]: getTabletIdColumn,
108+
[TOP_SHARDS_COLUMNS_IDS.NodeId]: getNodeIdColumn,
109+
[TOP_SHARDS_COLUMNS_IDS.CPUCores]: getCpuCoresColumn,
110+
[TOP_SHARDS_COLUMNS_IDS.InFlightTxCount]: getInFlightTxCountColumn,
111+
[TOP_SHARDS_COLUMNS_IDS.PeakTime]: getPeakTimeColumn,
112+
[TOP_SHARDS_COLUMNS_IDS.IntervalEnd]: getIntervalEndColumn,
113+
};

src/containers/Tenant/Diagnostics/TopShards/columns/constants.ts src/components/ShardsTable/constants.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {ValueOf} from '../../../../../types/common';
1+
import type {ValueOf} from '../../types/common';
22

33
import i18n from './i18n';
44

@@ -15,7 +15,7 @@ export const TOP_SHARDS_COLUMNS_IDS = {
1515
IntervalEnd: 'IntervalEnd',
1616
} as const;
1717

18-
type TopShardsColumnId = ValueOf<typeof TOP_SHARDS_COLUMNS_IDS>;
18+
export type TopShardsColumnId = ValueOf<typeof TOP_SHARDS_COLUMNS_IDS>;
1919

2020
export const TOP_SHARDS_COLUMNS_TITLES: Record<TopShardsColumnId, string> = {
2121
get TabletId() {
@@ -52,7 +52,7 @@ const TOP_SHARDS_COLUMNS_TO_SORT_FIELDS: Record<TopShardsColumnId, string | unde
5252
NodeId: undefined,
5353
PeakTime: undefined,
5454
InFlightTxCount: 'InFlightTxCount',
55-
IntervalEnd: undefined,
55+
IntervalEnd: 'IntervalEnd',
5656
} as const;
5757

5858
export function getTopShardsColumnSortField(columnId?: string) {
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {registerKeysets} from '../../../utils/i18n';
2+
3+
import en from './en.json';
4+
5+
const COMPONENT = 'ydb-shards-table';
6+
7+
export default registerKeysets(COMPONENT, {en});

src/components/ShardsTable/types.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type {Column} from '@gravity-ui/react-data-table';
2+
3+
import type {KeyValueRow} from '../../types/api/query';
4+
5+
export type ShardsColumn = Column<KeyValueRow>;
6+
7+
export type GetShardsColumn = (params: {database: string; schemaPath?: string}) => ShardsColumn;

src/components/ShardsTable/utils.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type {CellValue} from '../../types/api/query';
2+
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
3+
import {formatDateTime} from '../../utils/dataFormatters/dataFormatters';
4+
5+
export function prepareDateTimeValue(value: CellValue) {
6+
if (!value) {
7+
return EMPTY_DATA_PLACEHOLDER;
8+
}
9+
return formatDateTime(new Date(value).getTime());
10+
}

src/containers/Tenant/Diagnostics/TenantOverview/TenantCpu/TopShards.tsx

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import {useLocation} from 'react-router-dom';
22

3-
import {ResizeableDataTable} from '../../../../../components/ResizeableDataTable/ResizeableDataTable';
3+
import {useComponent} from '../../../../../components/ComponentsProvider/ComponentsProvider';
4+
import type {TopShardsColumnId} from '../../../../../components/ShardsTable/constants';
45
import {parseQuery} from '../../../../../routes';
56
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
67
import {topShardsApi} from '../../../../../store/reducers/tenantOverview/topShards/tenantOverviewTopShards';
78
import {TENANT_OVERVIEW_TABLES_SETTINGS} from '../../../../../utils/constants';
89
import {useAutoRefreshInterval} from '../../../../../utils/hooks';
910
import {parseQueryErrorToString} from '../../../../../utils/query';
1011
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
11-
import {getTopShardsColumns} from '../../TopShards/columns/columns';
12-
import {TOP_SHARDS_COLUMNS_WIDTH_LS_KEY} from '../../TopShards/columns/constants';
1312
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
1413
import {getSectionTitle} from '../getSectionTitle';
1514
import i18n from '../i18n';
1615

16+
const columnsIds: TopShardsColumnId[] = ['TabletId', 'Path', 'CPUCores'];
17+
1718
interface TopShardsProps {
1819
tenantName: string;
1920
path: string;
2021
}
2122

2223
export const TopShards = ({tenantName, path}: TopShardsProps) => {
24+
const ShardsTable = useComponent('ShardsTable');
25+
2326
const location = useLocation();
2427

2528
const query = parseQuery(location);
@@ -34,8 +37,6 @@ export const TopShards = ({tenantName, path}: TopShardsProps) => {
3437
const loading = isFetching && currentData === undefined;
3538
const data = currentData?.resultSets?.[0]?.result || [];
3639

37-
const columns = getTopShardsColumns(tenantName, location);
38-
3940
const title = getSectionTitle({
4041
entity: i18n('shards'),
4142
postfix: i18n('by-cpu-usage'),
@@ -52,10 +53,11 @@ export const TopShards = ({tenantName, path}: TopShardsProps) => {
5253
error={parseQueryErrorToString(error)}
5354
withData={Boolean(currentData)}
5455
>
55-
<ResizeableDataTable
56-
columnsWidthLSKey={TOP_SHARDS_COLUMNS_WIDTH_LS_KEY}
56+
<ShardsTable
5757
data={data}
58-
columns={columns}
58+
schemaPath={tenantName}
59+
database={tenantName}
60+
columnsIds={columnsIds}
5961
settings={TENANT_OVERVIEW_TABLES_SETTINGS}
6062
/>
6163
</TenantOverviewTableLayout>

src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopTables.tsx

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type {Column} from '@gravity-ui/react-data-table';
22
import DataTable from '@gravity-ui/react-data-table';
3-
import {useLocation} from 'react-router-dom';
43

54
import {CellWithPopover} from '../../../../../components/CellWithPopover/CellWithPopover';
65
import {LinkToSchemaObject} from '../../../../../components/LinkToSchemaObject/LinkToSchemaObject';
@@ -24,8 +23,6 @@ interface TopTablesProps {
2423
const TOP_TABLES_COLUMNS_WIDTH_LS_KEY = 'topTablesTableColumnsWidth';
2524

2625
export function TopTables({database}: TopTablesProps) {
27-
const location = useLocation();
28-
2926
const [autoRefreshInterval] = useAutoRefreshInterval();
3027

3128
const {currentData, error, isFetching} = topTablesApi.useGetTopTablesQuery(
@@ -55,9 +52,7 @@ export function TopTables({database}: TopTablesProps) {
5552
render: ({row}) =>
5653
row.Path ? (
5754
<CellWithPopover content={row.Path}>
58-
<LinkToSchemaObject path={String(row.Path)} location={location}>
59-
{row.Path}
60-
</LinkToSchemaObject>
55+
<LinkToSchemaObject path={String(row.Path)}>{row.Path}</LinkToSchemaObject>
6156
</CellWithPopover>
6257
) : null,
6358
},

0 commit comments

Comments
 (0)