Skip to content
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

feat: add ShardsTable to componentsRegistry #1993

Merged
merged 2 commits into from
Mar 10, 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
4 changes: 3 additions & 1 deletion src/components/ComponentsProvider/componentsRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {AsideNavigation} from '../../containers/AsideNavigation/AsideNavigation';
import {ErrorBoundaryInner} from '../ErrorBoundary/ErrorBoundary';
import {ShardsTable} from '../ShardsTable/ShardsTable';
import {StaffCard} from '../User/StaffCard';

import type {ComponentsRegistryTemplate} from './registry';
Expand All @@ -8,7 +9,8 @@ import {Registry} from './registry';
const componentsRegistryInner = new Registry()
.register('StaffCard', StaffCard)
.register('AsideNavigation', AsideNavigation)
.register('ErrorBoundary', ErrorBoundaryInner);
.register('ErrorBoundary', ErrorBoundaryInner)
.register('ShardsTable', ShardsTable);

export type ComponentsRegistry = ComponentsRegistryTemplate<typeof componentsRegistryInner>;

Expand Down
6 changes: 3 additions & 3 deletions src/components/LinkToSchemaObject/LinkToSchemaObject.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {Link} from '@gravity-ui/uikit';
import type {LinkProps} from '@gravity-ui/uikit';
import type {Location} from 'history';
import {useLocation} from 'react-router-dom';

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

interface LinkToSchemaObjectProps extends Omit<LinkProps, 'href'> {
path: string;
location: Location;
}

export function LinkToSchemaObject({path, location, ...props}: LinkToSchemaObjectProps) {
export function LinkToSchemaObject({path, ...props}: LinkToSchemaObjectProps) {
const location = useLocation();
const queryParams = parseQuery(location);
const pathToSchemaObject = createExternalUILink({
...queryParams,
Expand Down
41 changes: 41 additions & 0 deletions src/components/ShardsTable/ShardsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from 'react';

import type {KeyValueRow} from '../../types/api/query';
import type {ResizeableDataTableProps} from '../ResizeableDataTable/ResizeableDataTable';
import {ResizeableDataTable} from '../ResizeableDataTable/ResizeableDataTable';

import {shardsColumnIdToGetColumn} from './columns';
import type {TopShardsColumnId} from './constants';
import {TOP_SHARDS_COLUMNS_WIDTH_LS_KEY, isSortableTopShardsColumn} from './constants';

export interface ShardsTableProps
extends Omit<ResizeableDataTableProps<KeyValueRow>, 'columnsWidthLSKey' | 'columns'> {
columnsIds: TopShardsColumnId[];
database: string;
schemaPath?: string;
}

export function ShardsTable({columnsIds, schemaPath, database, ...props}: ShardsTableProps) {
const columns = React.useMemo(
() =>
columnsIds
.filter((id) => id in shardsColumnIdToGetColumn)
.map((id) => {
const column = shardsColumnIdToGetColumn[id]({database, schemaPath});

return {
...column,
sortable: isSortableTopShardsColumn(column.name),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add this prop in previous map cycle?

};
}),
[columnsIds, database, schemaPath],
);

return (
<ResizeableDataTable
{...props}
columnsWidthLSKey={TOP_SHARDS_COLUMNS_WIDTH_LS_KEY}
columns={columns}
/>
);
}
113 changes: 113 additions & 0 deletions src/components/ShardsTable/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import DataTable from '@gravity-ui/react-data-table';

import {getDefaultNodePath} from '../../containers/Node/NodePages';
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {formatNumber, roundToPrecision} from '../../utils/dataFormatters/dataFormatters';
import {getUsageSeverity} from '../../utils/generateEvaluator';
import {InternalLink} from '../InternalLink';
import {LinkToSchemaObject} from '../LinkToSchemaObject/LinkToSchemaObject';
import {TabletNameWrapper} from '../TabletNameWrapper/TabletNameWrapper';
import {UsageLabel} from '../UsageLabel/UsageLabel';

import type {TopShardsColumnId} from './constants';
import {TOP_SHARDS_COLUMNS_IDS, TOP_SHARDS_COLUMNS_TITLES} from './constants';
import type {GetShardsColumn} from './types';
import {prepareDateTimeValue} from './utils';

export const getPathColumn: GetShardsColumn = ({schemaPath = ''}) => {
return {
name: TOP_SHARDS_COLUMNS_IDS.Path,
header: TOP_SHARDS_COLUMNS_TITLES.Path,
render: ({row}) => {
// row.Path - relative schema path
return <LinkToSchemaObject path={schemaPath + row.Path}>{row.Path}</LinkToSchemaObject>;
},
width: 300,
};
};
export const getDataSizeColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.DataSize,
header: TOP_SHARDS_COLUMNS_TITLES.DataSize,
render: ({row}) => {
return formatNumber(row.DataSize);
},
align: DataTable.RIGHT,
};
};
export const getTabletIdColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.TabletId,
header: TOP_SHARDS_COLUMNS_TITLES.TabletId,
render: ({row}) => {
if (!row.TabletId) {
return EMPTY_DATA_PLACEHOLDER;
}
return <TabletNameWrapper tabletId={row.TabletId} />;
},
width: 220,
};
};
export const getNodeIdColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.NodeId,
header: TOP_SHARDS_COLUMNS_TITLES.NodeId,
render: ({row}) => {
if (!row.NodeId) {
return EMPTY_DATA_PLACEHOLDER;
}
return <InternalLink to={getDefaultNodePath(row.NodeId)}>{row.NodeId}</InternalLink>;
},
align: DataTable.RIGHT,
};
};
export const getCpuCoresColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.CPUCores,
header: TOP_SHARDS_COLUMNS_TITLES.CPUCores,
render: ({row}) => {
const usage = Number(row.CPUCores) * 100 || 0;
return (
<UsageLabel value={roundToPrecision(usage, 2)} theme={getUsageSeverity(usage)} />
);
},
align: DataTable.RIGHT,
width: 110,
resizeMinWidth: 110,
};
};
export const getInFlightTxCountColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.InFlightTxCount,
header: TOP_SHARDS_COLUMNS_TITLES.InFlightTxCount,
render: ({row}) => formatNumber(row.InFlightTxCount),
align: DataTable.RIGHT,
};
};
export const getPeakTimeColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.PeakTime,
render: ({row}) => {
return prepareDateTimeValue(row.PeakTime);
},
};
};
export const getIntervalEndColumn: GetShardsColumn = () => {
return {
name: TOP_SHARDS_COLUMNS_IDS.IntervalEnd,
render: ({row}) => {
return prepareDateTimeValue(row.IntervalEnd);
},
};
};

export const shardsColumnIdToGetColumn: Record<TopShardsColumnId, GetShardsColumn> = {
[TOP_SHARDS_COLUMNS_IDS.Path]: getPathColumn,
[TOP_SHARDS_COLUMNS_IDS.DataSize]: getDataSizeColumn,
[TOP_SHARDS_COLUMNS_IDS.TabletId]: getTabletIdColumn,
[TOP_SHARDS_COLUMNS_IDS.NodeId]: getNodeIdColumn,
[TOP_SHARDS_COLUMNS_IDS.CPUCores]: getCpuCoresColumn,
[TOP_SHARDS_COLUMNS_IDS.InFlightTxCount]: getInFlightTxCountColumn,
[TOP_SHARDS_COLUMNS_IDS.PeakTime]: getPeakTimeColumn,
[TOP_SHARDS_COLUMNS_IDS.IntervalEnd]: getIntervalEndColumn,
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {ValueOf} from '../../../../../types/common';
import type {ValueOf} from '../../types/common';

import i18n from './i18n';

Expand All @@ -15,7 +15,7 @@ export const TOP_SHARDS_COLUMNS_IDS = {
IntervalEnd: 'IntervalEnd',
} as const;

type TopShardsColumnId = ValueOf<typeof TOP_SHARDS_COLUMNS_IDS>;
export type TopShardsColumnId = ValueOf<typeof TOP_SHARDS_COLUMNS_IDS>;

export const TOP_SHARDS_COLUMNS_TITLES: Record<TopShardsColumnId, string> = {
get TabletId() {
Expand Down Expand Up @@ -52,7 +52,7 @@ const TOP_SHARDS_COLUMNS_TO_SORT_FIELDS: Record<TopShardsColumnId, string | unde
NodeId: undefined,
PeakTime: undefined,
InFlightTxCount: 'InFlightTxCount',
IntervalEnd: undefined,
IntervalEnd: 'IntervalEnd',
} as const;

export function getTopShardsColumnSortField(columnId?: string) {
Expand Down
7 changes: 7 additions & 0 deletions src/components/ShardsTable/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {registerKeysets} from '../../../utils/i18n';

import en from './en.json';

const COMPONENT = 'ydb-shards-table';

export default registerKeysets(COMPONENT, {en});
7 changes: 7 additions & 0 deletions src/components/ShardsTable/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type {Column} from '@gravity-ui/react-data-table';

import type {KeyValueRow} from '../../types/api/query';

export type ShardsColumn = Column<KeyValueRow>;

export type GetShardsColumn = (params: {database: string; schemaPath?: string}) => ShardsColumn;
10 changes: 10 additions & 0 deletions src/components/ShardsTable/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type {CellValue} from '../../types/api/query';
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
import {formatDateTime} from '../../utils/dataFormatters/dataFormatters';

export function prepareDateTimeValue(value: CellValue) {
if (!value) {
return EMPTY_DATA_PLACEHOLDER;
}
return formatDateTime(new Date(value).getTime());
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
import {useLocation} from 'react-router-dom';

import {ResizeableDataTable} from '../../../../../components/ResizeableDataTable/ResizeableDataTable';
import {useComponent} from '../../../../../components/ComponentsProvider/ComponentsProvider';
import type {TopShardsColumnId} from '../../../../../components/ShardsTable/constants';
import {parseQuery} from '../../../../../routes';
import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants';
import {topShardsApi} from '../../../../../store/reducers/tenantOverview/topShards/tenantOverviewTopShards';
import {TENANT_OVERVIEW_TABLES_SETTINGS} from '../../../../../utils/constants';
import {useAutoRefreshInterval} from '../../../../../utils/hooks';
import {parseQueryErrorToString} from '../../../../../utils/query';
import {TenantTabsGroups, getTenantPath} from '../../../TenantPages';
import {getTopShardsColumns} from '../../TopShards/columns/columns';
import {TOP_SHARDS_COLUMNS_WIDTH_LS_KEY} from '../../TopShards/columns/constants';
import {TenantOverviewTableLayout} from '../TenantOverviewTableLayout';
import {getSectionTitle} from '../getSectionTitle';
import i18n from '../i18n';

const columnsIds: TopShardsColumnId[] = ['TabletId', 'Path', 'CPUCores'];

interface TopShardsProps {
tenantName: string;
path: string;
}

export const TopShards = ({tenantName, path}: TopShardsProps) => {
const ShardsTable = useComponent('ShardsTable');

const location = useLocation();

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

const columns = getTopShardsColumns(tenantName, location);

const title = getSectionTitle({
entity: i18n('shards'),
postfix: i18n('by-cpu-usage'),
Expand All @@ -52,10 +53,11 @@ export const TopShards = ({tenantName, path}: TopShardsProps) => {
error={parseQueryErrorToString(error)}
withData={Boolean(currentData)}
>
<ResizeableDataTable
columnsWidthLSKey={TOP_SHARDS_COLUMNS_WIDTH_LS_KEY}
<ShardsTable
data={data}
columns={columns}
schemaPath={tenantName}
database={tenantName}
columnsIds={columnsIds}
settings={TENANT_OVERVIEW_TABLES_SETTINGS}
/>
</TenantOverviewTableLayout>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type {Column} from '@gravity-ui/react-data-table';
import DataTable from '@gravity-ui/react-data-table';
import {useLocation} from 'react-router-dom';

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

export function TopTables({database}: TopTablesProps) {
const location = useLocation();

const [autoRefreshInterval] = useAutoRefreshInterval();

const {currentData, error, isFetching} = topTablesApi.useGetTopTablesQuery(
Expand Down Expand Up @@ -55,9 +52,7 @@ export function TopTables({database}: TopTablesProps) {
render: ({row}) =>
row.Path ? (
<CellWithPopover content={row.Path}>
<LinkToSchemaObject path={String(row.Path)} location={location}>
{row.Path}
</LinkToSchemaObject>
<LinkToSchemaObject path={String(row.Path)}>{row.Path}</LinkToSchemaObject>
</CellWithPopover>
) : null,
},
Expand Down
Loading
Loading