From cf6a510d12a47ba151b6cd6fdd67f0391ecd3cbf Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Tue, 19 Mar 2024 15:18:01 +0100 Subject: [PATCH 01/15] docs: update the command to run local Docker --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6936722db3..74f1ed4f77 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,14 @@ Open http://localhost:8765 to view it in the browser. 1. Run on a machine with Docker installed: ``` - docker pull cr.yandex/yc/yandex-docker-local-ydb - docker run --hostname localhost -e YDB_ALLOW_ORIGIN="http://localhost:3000" -dp 2135:2135 -dp 8765:8765 cr.yandex/yc/yandex-docker-local-ydb + docker run --rm -ti --name ydb-local -h localhost \ + -p 2135:2135 -p 2136:2136 -p 8765:8765 \ + -v $(pwd)/ydb_certs:/ydb_certs -v $(pwd)/ydb_data:/ydb_data \ + -e GRPC_TLS_PORT=2135 -e GRPC_PORT=2136 -e MON_PORT=8765 \ + cr.yandex/yc/yandex-docker-local-ydb:latest ``` 2. Run the frontend app in the development mode, via invoking `npm run dev` -3. Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\ +3. Open [localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.\ You will also see any lint errors in the console. For API reference, open Swagger UI on http://localhost:8765/viewer/api/. @@ -37,7 +40,9 @@ For API reference, open Swagger UI on http://localhost:8765/viewer/api/. Image `cr.yandex/yc/yandex-docker-local-ydb` corresponds to `:latest` tag. It's the latest stable ydb version. -To test new features, you can use ydb version that is currently in testing mode with `cr.yandex/yc/yandex-docker-local-ydb:edge` image. Also you can set specific version like `cr.yandex/yc/yandex-docker-local-ydb:23.1` +To test new features, you can use ydb version that is currently in testing mode with `cr.yandex/yc/yandex-docker-local-ydb:edge` image +or use a build from `main` brunch with `ghcr.io/ydb-platform/local-ydb:trunk` image. +Also you can set specific version like `cr.yandex/yc/yandex-docker-local-ydb:23.1` ### Custom backend in dev mode From 4c815c1e3f8238ff1fca568c0eed52a509ac00c9 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Tue, 19 Mar 2024 15:20:12 +0100 Subject: [PATCH 02/15] feat(Diagnostics): add Schema tab with column family info --- src/containers/Tenant/Acl/Acl.scss | 5 - src/containers/Tenant/Acl/Acl.tsx | 4 +- .../Tenant/Diagnostics/Diagnostics.tsx | 6 +- .../Tenant/Diagnostics/DiagnosticsPages.ts | 9 +- .../Tenant/ObjectSummary/ObjectSummary.tsx | 59 +------- .../Schema/SchemaViewer/SchemaViewer.scss | 13 +- .../Schema/SchemaViewer/SchemaViewer.tsx | 139 +++++++++++++++--- src/store/reducers/tenant/constants.ts | 1 + src/types/api/schema/table.ts | 2 +- 9 files changed, 152 insertions(+), 86 deletions(-) diff --git a/src/containers/Tenant/Acl/Acl.scss b/src/containers/Tenant/Acl/Acl.scss index 3c59c58ab6..7104eaa276 100644 --- a/src/containers/Tenant/Acl/Acl.scss +++ b/src/containers/Tenant/Acl/Acl.scss @@ -5,17 +5,12 @@ overflow: auto; flex-grow: 1; - padding: 0 12px 16px; @include query-data-table; &__result { align-self: flex-start; } - &__message-container { - padding: 0 12px 16px; - } - &__owner-container { position: sticky; z-index: 2; diff --git a/src/containers/Tenant/Acl/Acl.tsx b/src/containers/Tenant/Acl/Acl.tsx index 3dafd055a1..11b5ec74a1 100644 --- a/src/containers/Tenant/Acl/Acl.tsx +++ b/src/containers/Tenant/Acl/Acl.tsx @@ -118,11 +118,11 @@ export const Acl = () => { } if (error) { - return ; + return ; } if (!loading && !acl && !owner) { - return
{i18n('acl.empty')}
; + return <>{i18n('acl.empty')}; } return ( diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx index 962176b9dd..1a370e10cd 100644 --- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx +++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx @@ -19,8 +19,9 @@ import {Heatmap} from '../../Heatmap'; import {NodesWrapper} from '../../Nodes/NodesWrapper'; import {StorageWrapper} from '../../Storage/StorageWrapper'; import {Tablets} from '../../Tablets'; -import {TenantTabsGroups} from '../TenantPages'; import {isDatabaseEntityType} from '../utils/schema'; +import {TenantTabsGroups} from '../TenantPages'; +import SchemaViewer from '../Schema/SchemaViewer/SchemaViewer'; import {AutorefreshControl} from './Autorefresh/AutorefreshControl'; import {Consumers} from './Consumers'; @@ -103,6 +104,9 @@ function Diagnostics(props: DiagnosticsProps) { /> ); } + case TENANT_DIAGNOSTICS_TABS_IDS.schema: { + return ; + } case TENANT_DIAGNOSTICS_TABS_IDS.topQueries: { return ; } diff --git a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts index 8316c6780b..8eb5cc7536 100644 --- a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts +++ b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts @@ -12,6 +12,11 @@ const overview = { title: 'Info', }; +const schema = { + id: TENANT_DIAGNOSTICS_TABS_IDS.schema, + title: 'Schema', +}; + const topQueries = { id: TENANT_DIAGNOSTICS_TABS_IDS.topQueries, title: 'Top queries', @@ -76,8 +81,8 @@ export const DATABASE_PAGES = [ describe, ]; -export const TABLE_PAGES = [overview, topShards, nodes, graph, tablets, hotKeys, describe]; -export const COLUMN_TABLE_PAGES = [overview, topShards, nodes, graph, tablets, describe]; +export const TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, hotKeys, describe]; +export const COLUMN_TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, describe]; export const DIR_PAGES = [overview, topShards, nodes, describe]; diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index b7c21eb5b9..e6dfb9c653 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -23,11 +23,7 @@ import { TENANT_SUMMARY_TABS_IDS, } from '../../../store/reducers/tenant/constants'; import {setQueryTab, setSummaryTab, setTenantPage} from '../../../store/reducers/tenant/tenant'; -import type { - EPathSubType, - TColumnDescription, - TColumnTableDescription, -} from '../../../types/api/schema'; +import type {EPathSubType} from '../../../types/api/schema'; import {EPathType} from '../../../types/api/schema'; import {cn} from '../../../utils/cn'; import { @@ -48,7 +44,7 @@ import { PaneVisibilityToggleButtons, paneVisibilityToggleReducerCreator, } from '../utils/paneVisibilityToggleHelpers'; -import {isColumnEntityType, isExternalTable, isIndexTable, isTableType} from '../utils/schema'; +import {isIndexTable, isTableType} from '../utils/schema'; import './ObjectSummary.scss'; @@ -64,29 +60,6 @@ const getTenantCommonInfoState = () => { }; }; -function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { - const {Name, Schema} = tableSchema; - - if (Schema) { - const {Columns, KeyColumnNames} = Schema; - const KeyColumnIds = KeyColumnNames?.map((name: string) => { - const column = Columns?.find((el) => el.Name === name); - return column?.Id; - }).filter((id): id is number => id !== undefined); - - return { - Columns, - KeyColumnNames, - Name, - KeyColumnIds, - }; - } - - return { - Name, - }; -} - interface ObjectSummaryProps { type?: EPathType; subType?: EPathSubType; @@ -112,7 +85,6 @@ export function ObjectSummary({ data, currentSchemaPath, currentSchema: currentItem = {}, - loading: loadingSchema, } = useTypedSelector((state) => state.schema); const {summaryTab = TENANT_SUMMARY_TABS_IDS.overview} = useTypedSelector( (state) => state.tenant, @@ -130,21 +102,6 @@ export function ObjectSummary({ const currentObjectData = currentSchemaPath ? data[currentSchemaPath] : undefined; const currentSchemaData = currentObjectData?.PathDescription?.Self; - let keyColumnIds: number[] | undefined; - let columns: TColumnDescription[] | undefined; - - if (isTableType(type) && isColumnEntityType(type)) { - const description = currentObjectData?.PathDescription?.ColumnTableDescription; - const columnTableSchema = prepareOlapTableSchema(description); - keyColumnIds = columnTableSchema.KeyColumnIds; - columns = columnTableSchema.Columns; - } else if (isExternalTable(type)) { - columns = currentObjectData?.PathDescription?.ExternalTableDescription?.Columns; - } else { - keyColumnIds = currentObjectData?.PathDescription?.Table?.KeyColumnIds; - columns = currentObjectData?.PathDescription?.Table?.Columns; - } - React.useEffect(() => { const isTable = isTableType(type); @@ -218,7 +175,7 @@ export function ObjectSummary({ component = ; } - return
{component}
; + return component; }; const renderLoader = () => { @@ -236,12 +193,8 @@ export function ObjectSummary({ return ; } case TENANT_SUMMARY_TABS_IDS.schema: { - return loadingSchema ? ( - renderLoader() - ) : ( -
- -
+ return ( + ); } default: { @@ -364,7 +317,7 @@ export function ObjectSummary({ {renderTabs()} - {renderTabContent()} +
{renderTabContent()}
diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss index b782e5ccbb..b49c7402c8 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss @@ -1,7 +1,18 @@ .schema-viewer { - padding: 0px 12px; &__key-icon { display: flex; align-items: center; } + + &__skeleton { + display: flex; + flex-direction: column; + + width: 100%; + gap: 8px; + } + + &__skeleton-item { + height: 40px; + } } diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index c1a3018d61..2fd5232c31 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -4,12 +4,19 @@ import type {Column} from '@gravity-ui/react-data-table'; import DataTable from '@gravity-ui/react-data-table'; import {Icon} from '../../../../components/Icon'; -import type {EPathType, TColumnDescription} from '../../../../types/api/schema'; +import type { + EPathType, + TColumnDescription, + TColumnTableDescription, + TFamilyDescription, +} from '../../../../types/api/schema'; import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; -import {isExternalTable} from '../../utils/schema'; +import {isColumnEntityType, isExternalTable, isTableType} from '../../utils/schema'; import './SchemaViewer.scss'; +import {useTypedSelector} from '../../../../utils/hooks'; +import {Skeleton} from '@gravity-ui/uikit'; const b = cn('schema-viewer'); @@ -19,15 +26,68 @@ const SchemaViewerColumns = { key: 'Key', type: 'Type', notNull: 'NotNull', + familyName: 'FamilyName', + preferredPoolKind: 'PreferredPoolKind', + columnCodec: 'ColumnCodec', }; interface SchemaViewerProps { - keyColumnIds?: number[]; - columns?: TColumnDescription[]; + className?: string; type?: EPathType; + path?: string; + withFamilies?: boolean; } -export const SchemaViewer = ({keyColumnIds = [], columns = [], type}: SchemaViewerProps) => { +function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { + const {Name, Schema} = tableSchema; + + if (Schema) { + const {Columns, KeyColumnNames} = Schema; + const KeyColumnIds = KeyColumnNames?.map((name: string) => { + const column = Columns?.find((el) => el.Name === name); + return column?.Id; + }).filter((id): id is number => id !== undefined); + + return { + Columns, + KeyColumnNames, + Name, + KeyColumnIds, + }; + } + + return { + Name, + }; +} + +export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewerProps) => { + const {data, loading} = useTypedSelector((state) => state.schema); + const currentObjectData = path ? data[path] : undefined; + + let keyColumnIds: number[] = []; + let columns: TColumnDescription[] = []; + + if (isTableType(type) && isColumnEntityType(type)) { + const description = currentObjectData?.PathDescription?.ColumnTableDescription; + const columnTableSchema = prepareOlapTableSchema(description); + keyColumnIds = columnTableSchema.KeyColumnIds ?? []; + columns = columnTableSchema.Columns ?? []; + } else if (isExternalTable(type)) { + columns = currentObjectData?.PathDescription?.ExternalTableDescription?.Columns ?? []; + } else { + keyColumnIds = currentObjectData?.PathDescription?.Table?.KeyColumnIds ?? []; + columns = currentObjectData?.PathDescription?.Table?.Columns ?? []; + } + + const families = + currentObjectData?.PathDescription?.Table?.PartitionConfig?.ColumnFamilies?.reduce< + Record + >((acc, family) => { + if (family.Id) acc[family.Id] = family; + return acc; + }, {}) ?? {}; + // Keys should be displayd by their order in keyColumnIds (Primary Key) const keyColumnsOrderValues = React.useMemo(() => { return keyColumnIds.reduce>((result, keyColumnId, index) => { @@ -38,12 +98,16 @@ export const SchemaViewer = ({keyColumnIds = [], columns = [], type}: SchemaView }, {}); }, [keyColumnIds]); - let dataTableColumns: Column[] = [ + const dataTableColumns: Column[] = [ { name: SchemaViewerColumns.id, width: 40, }, - { + ]; + + if (!isExternalTable(type)) { + // External tables don't have key columns + dataTableColumns.push({ name: SchemaViewerColumns.key, width: 40, // Table should start with key columns on sort click @@ -59,7 +123,10 @@ export const SchemaViewer = ({keyColumnIds = [], columns = [], type}: SchemaView ) : null; }, - }, + }); + } + + dataTableColumns.push( { name: SchemaViewerColumns.name, width: 100, @@ -81,24 +148,54 @@ export const SchemaViewer = ({keyColumnIds = [], columns = [], type}: SchemaView return undefined; }, }, - ]; + ); - if (isExternalTable(type)) { - // External tables don't have key columns - dataTableColumns = dataTableColumns.filter( - (column) => column.name !== SchemaViewerColumns.key, + if (withFamilies) { + dataTableColumns.push( + { + name: SchemaViewerColumns.familyName, + width: 100, + sortable: false, + render: ({row}) => (row.Family ? families[row.Family].Name : undefined), + }, + { + name: SchemaViewerColumns.preferredPoolKind, + width: 100, + sortable: false, + render: ({row}) => + row.Family + ? families[row.Family].StorageConfig?.Data?.PreferredPoolKind + : undefined, + }, + { + name: SchemaViewerColumns.columnCodec, + width: 100, + sortable: false, + render: ({row}) => (row.Family ? families[row.Family].ColumnCodec : undefined), + }, ); } return ( -
- +
+ {loading ? ( +
+ + + +
+ ) : ( + + )}
); }; diff --git a/src/store/reducers/tenant/constants.ts b/src/store/reducers/tenant/constants.ts index 3d6a297b04..d7818309a2 100644 --- a/src/store/reducers/tenant/constants.ts +++ b/src/store/reducers/tenant/constants.ts @@ -13,6 +13,7 @@ export const TENANT_QUERY_TABS_ID = { export const TENANT_DIAGNOSTICS_TABS_IDS = { overview: 'overview', + schema: 'schema', topQueries: 'topQueries', topShards: 'topShards', nodes: 'nodes', diff --git a/src/types/api/schema/table.ts b/src/types/api/schema/table.ts index 44b30ca663..86ec247b47 100644 --- a/src/types/api/schema/table.ts +++ b/src/types/api/schema/table.ts @@ -334,7 +334,7 @@ interface TPipelineConfig { EnableSoftUpdates?: boolean; } -interface TFamilyDescription { +export interface TFamilyDescription { Id?: number; Room?: number; /** @deprecated use ColumnCodec */ From 59be8a3f55feaa82c78661cd895308aff0c324e0 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Wed, 27 Mar 2024 18:00:37 +0100 Subject: [PATCH 03/15] fix(Diagnostics): disable family schema for non-row tables --- .../Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 8 ++++---- src/containers/Tenant/utils/schema.ts | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 2fd5232c31..599d681baa 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import type {Column} from '@gravity-ui/react-data-table'; +import {Skeleton} from '@gravity-ui/uikit'; import DataTable from '@gravity-ui/react-data-table'; +import type {Column} from '@gravity-ui/react-data-table'; import {Icon} from '../../../../components/Icon'; import type { @@ -12,11 +13,10 @@ import type { } from '../../../../types/api/schema'; import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; -import {isColumnEntityType, isExternalTable, isTableType} from '../../utils/schema'; +import {isColumnEntityType, isExternalTable, isRowTable, isTableType} from '../../utils/schema'; import './SchemaViewer.scss'; import {useTypedSelector} from '../../../../utils/hooks'; -import {Skeleton} from '@gravity-ui/uikit'; const b = cn('schema-viewer'); @@ -150,7 +150,7 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer }, ); - if (withFamilies) { + if (withFamilies && isRowTable(type)) { dataTableColumns.push( { name: SchemaViewerColumns.familyName, diff --git a/src/containers/Tenant/utils/schema.ts b/src/containers/Tenant/utils/schema.ts index fd77fa5e98..e24d2dc0ea 100644 --- a/src/containers/Tenant/utils/schema.ts +++ b/src/containers/Tenant/utils/schema.ts @@ -245,3 +245,4 @@ export const isPathTypeWithTopic = (type?: EPathType) => // ==================== export const isExternalTable = (type?: EPathType) => type === EPathType.EPathTypeExternalTable; +export const isRowTable = (type?: EPathType) => type === EPathType.EPathTypeTable; From 8895734b3ca7ae40c6763692749f466de0ceadc7 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Wed, 27 Mar 2024 18:04:50 +0100 Subject: [PATCH 04/15] fix(Diagnostics): rename schema columns and add codec formatting --- .../Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 599d681baa..fa9f320d05 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -5,6 +5,7 @@ import DataTable from '@gravity-ui/react-data-table'; import type {Column} from '@gravity-ui/react-data-table'; import {Icon} from '../../../../components/Icon'; +import {EColumnCodec} from '../../../../types/api/schema'; import type { EPathType, TColumnDescription, @@ -26,9 +27,9 @@ const SchemaViewerColumns = { key: 'Key', type: 'Type', notNull: 'NotNull', - familyName: 'FamilyName', - preferredPoolKind: 'PreferredPoolKind', - columnCodec: 'ColumnCodec', + familyName: 'Family', + preferredPoolKind: 'Media', + columnCodec: 'Compression', }; interface SchemaViewerProps { @@ -61,6 +62,12 @@ function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { }; } +function formatColumnCodec(codec?: EColumnCodec) { + if (!codec) return null; + if (codec === EColumnCodec.ColumnCodecPlain) return 'None'; + return codec.replace('ColumnCodec', '').toLocaleLowerCase(); +} + export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewerProps) => { const {data, loading} = useTypedSelector((state) => state.schema); const currentObjectData = path ? data[path] : undefined; @@ -171,7 +178,8 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer name: SchemaViewerColumns.columnCodec, width: 100, sortable: false, - render: ({row}) => (row.Family ? families[row.Family].ColumnCodec : undefined), + render: ({row}) => + row.Family ? formatColumnCodec(families[row.Family].ColumnCodec) : undefined, }, ); } From 56a4adf86a79f88f2615f2e849cf0dfcd6f6b8cf Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Wed, 27 Mar 2024 18:06:07 +0100 Subject: [PATCH 05/15] style: remove default imports --- src/containers/Tenant/Diagnostics/Diagnostics.tsx | 2 +- src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx index 1a370e10cd..5594836340 100644 --- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx +++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx @@ -21,7 +21,7 @@ import {StorageWrapper} from '../../Storage/StorageWrapper'; import {Tablets} from '../../Tablets'; import {isDatabaseEntityType} from '../utils/schema'; import {TenantTabsGroups} from '../TenantPages'; -import SchemaViewer from '../Schema/SchemaViewer/SchemaViewer'; +import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer'; import {AutorefreshControl} from './Autorefresh/AutorefreshControl'; import {Consumers} from './Consumers'; diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index fa9f320d05..8f8df1f145 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -207,5 +207,3 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer
); }; - -export default SchemaViewer; From db033dbfcb8943202bdd959a78c32b16f3f4ad02 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Wed, 27 Mar 2024 18:09:59 +0100 Subject: [PATCH 06/15] style: replace custom skeleton to `TableSkeleton` --- .../Tenant/Schema/SchemaViewer/SchemaViewer.scss | 8 -------- .../Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 10 +++------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss index b49c7402c8..ad15da437a 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss @@ -4,14 +4,6 @@ align-items: center; } - &__skeleton { - display: flex; - flex-direction: column; - - width: 100%; - gap: 8px; - } - &__skeleton-item { height: 40px; } diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 8f8df1f145..d2f317faa6 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import {Skeleton} from '@gravity-ui/uikit'; import DataTable from '@gravity-ui/react-data-table'; import type {Column} from '@gravity-ui/react-data-table'; @@ -14,10 +13,11 @@ import type { } from '../../../../types/api/schema'; import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; +import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; +import {useTypedSelector} from '../../../../utils/hooks'; import {isColumnEntityType, isExternalTable, isRowTable, isTableType} from '../../utils/schema'; import './SchemaViewer.scss'; -import {useTypedSelector} from '../../../../utils/hooks'; const b = cn('schema-viewer'); @@ -187,11 +187,7 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer return (
{loading ? ( -
- - - -
+ ) : ( Date: Wed, 27 Mar 2024 18:24:21 +0100 Subject: [PATCH 07/15] fix: add sorting to families --- .../Schema/SchemaViewer/SchemaViewer.tsx | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index d2f317faa6..ab4467859c 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -68,6 +68,14 @@ function formatColumnCodec(codec?: EColumnCodec) { return codec.replace('ColumnCodec', '').toLocaleLowerCase(); } +function comparePossiblyEmptyStrings(a?: string, b?: string): number { + if (a === undefined && b === undefined) return 0; + if (a === undefined) return 1; + if (b === undefined) return -1; + + return a.localeCompare(b); +} + export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewerProps) => { const {data, loading} = useTypedSelector((state) => state.schema); const currentObjectData = path ? data[path] : undefined; @@ -119,10 +127,8 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer width: 40, // Table should start with key columns on sort click defaultOrder: DataTable.ASCENDING, - sortAccessor: (row) => { - // Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns - return (row.Id && keyColumnsOrderValues[row.Id]) || 1; - }, + // Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns + sortAccessor: (row) => (row.Id && keyColumnsOrderValues[row.Id]) || 1, render: ({row}) => { return row.Id && keyColumnIds.includes(row.Id) ? (
@@ -162,24 +168,40 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer { name: SchemaViewerColumns.familyName, width: 100, - sortable: false, render: ({row}) => (row.Family ? families[row.Family].Name : undefined), + sortAscending: ({row: rowA}, {row: rowB}) => + comparePossiblyEmptyStrings( + rowA.Family ? families[rowA.Family].Name : undefined, + rowB.Family ? families[rowB.Family].Name : undefined, + ), }, { name: SchemaViewerColumns.preferredPoolKind, width: 100, - sortable: false, render: ({row}) => row.Family ? families[row.Family].StorageConfig?.Data?.PreferredPoolKind : undefined, + sortAscending: ({row: rowA}, {row: rowB}) => + comparePossiblyEmptyStrings( + rowA.Family + ? families[rowA.Family].StorageConfig?.Data?.PreferredPoolKind + : undefined, + rowB.Family + ? families[rowB.Family].StorageConfig?.Data?.PreferredPoolKind + : undefined, + ), }, { name: SchemaViewerColumns.columnCodec, width: 100, - sortable: false, render: ({row}) => row.Family ? formatColumnCodec(families[row.Family].ColumnCodec) : undefined, + sortAscending: ({row: rowA}, {row: rowB}) => + comparePossiblyEmptyStrings( + rowA.Family ? families[rowA.Family].ColumnCodec : undefined, + rowB.Family ? families[rowB.Family].ColumnCodec : undefined, + ), }, ); } From a44ea6dc427a33aad24cba554fcdba3535817d02 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Wed, 27 Mar 2024 18:27:49 +0100 Subject: [PATCH 08/15] docs: fix `docker run` command --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 74f1ed4f77..7c7e3bdace 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,8 @@ Open http://localhost:8765 to view it in the browser. 1. Run on a machine with Docker installed: ``` docker run --rm -ti --name ydb-local -h localhost \ - -p 2135:2135 -p 2136:2136 -p 8765:8765 \ - -v $(pwd)/ydb_certs:/ydb_certs -v $(pwd)/ydb_data:/ydb_data \ - -e GRPC_TLS_PORT=2135 -e GRPC_PORT=2136 -e MON_PORT=8765 \ + -p 8765:8765 \ + -e MON_PORT=8765 \ cr.yandex/yc/yandex-docker-local-ydb:latest ``` 2. Run the frontend app in the development mode, via invoking `npm run dev` From d457f359b808d77d9d94e422342c53af61a85e60 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Tue, 16 Apr 2024 11:21:57 +0200 Subject: [PATCH 09/15] style: fix linter issues --- src/containers/Tenant/Acl/Acl.tsx | 2 +- src/containers/Tenant/Diagnostics/Diagnostics.tsx | 4 ++-- src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/containers/Tenant/Acl/Acl.tsx b/src/containers/Tenant/Acl/Acl.tsx index 11b5ec74a1..c1304428e0 100644 --- a/src/containers/Tenant/Acl/Acl.tsx +++ b/src/containers/Tenant/Acl/Acl.tsx @@ -122,7 +122,7 @@ export const Acl = () => { } if (!loading && !acl && !owner) { - return <>{i18n('acl.empty')}; + return {i18n('acl.empty')}; } return ( diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx index 5594836340..1b56b61dc4 100644 --- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx +++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx @@ -19,9 +19,9 @@ import {Heatmap} from '../../Heatmap'; import {NodesWrapper} from '../../Nodes/NodesWrapper'; import {StorageWrapper} from '../../Storage/StorageWrapper'; import {Tablets} from '../../Tablets'; -import {isDatabaseEntityType} from '../utils/schema'; -import {TenantTabsGroups} from '../TenantPages'; import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer'; +import {TenantTabsGroups} from '../TenantPages'; +import {isDatabaseEntityType} from '../utils/schema'; import {AutorefreshControl} from './Autorefresh/AutorefreshControl'; import {Consumers} from './Consumers'; diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index ab4467859c..0af7f015ba 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -4,6 +4,7 @@ import DataTable from '@gravity-ui/react-data-table'; import type {Column} from '@gravity-ui/react-data-table'; import {Icon} from '../../../../components/Icon'; +import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; import {EColumnCodec} from '../../../../types/api/schema'; import type { EPathType, @@ -13,7 +14,6 @@ import type { } from '../../../../types/api/schema'; import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; -import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; import {useTypedSelector} from '../../../../utils/hooks'; import {isColumnEntityType, isExternalTable, isRowTable, isTableType} from '../../utils/schema'; From 934f63ed1367b72f2e34328b6a1f12727c7c79aa Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Tue, 16 Apr 2024 11:27:57 +0200 Subject: [PATCH 10/15] style: remove unused style --- src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss index ad15da437a..1e07dadc0c 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss @@ -3,8 +3,4 @@ display: flex; align-items: center; } - - &__skeleton-item { - height: 40px; - } } From 994150839feb408db6c0bcb188ffa563702fad3b Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Tue, 16 Apr 2024 14:29:11 +0200 Subject: [PATCH 11/15] fix: schema columns sorting accessor --- .../Schema/SchemaViewer/SchemaViewer.tsx | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 0af7f015ba..f7d528b1bd 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -68,14 +68,6 @@ function formatColumnCodec(codec?: EColumnCodec) { return codec.replace('ColumnCodec', '').toLocaleLowerCase(); } -function comparePossiblyEmptyStrings(a?: string, b?: string): number { - if (a === undefined && b === undefined) return 0; - if (a === undefined) return 1; - if (b === undefined) return -1; - - return a.localeCompare(b); -} - export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewerProps) => { const {data, loading} = useTypedSelector((state) => state.schema); const currentObjectData = path ? data[path] : undefined; @@ -169,11 +161,7 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer name: SchemaViewerColumns.familyName, width: 100, render: ({row}) => (row.Family ? families[row.Family].Name : undefined), - sortAscending: ({row: rowA}, {row: rowB}) => - comparePossiblyEmptyStrings( - rowA.Family ? families[rowA.Family].Name : undefined, - rowB.Family ? families[rowB.Family].Name : undefined, - ), + sortAccessor: (row) => (row.Family ? families[row.Family].Name : undefined), }, { name: SchemaViewerColumns.preferredPoolKind, @@ -182,26 +170,17 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer row.Family ? families[row.Family].StorageConfig?.Data?.PreferredPoolKind : undefined, - sortAscending: ({row: rowA}, {row: rowB}) => - comparePossiblyEmptyStrings( - rowA.Family - ? families[rowA.Family].StorageConfig?.Data?.PreferredPoolKind - : undefined, - rowB.Family - ? families[rowB.Family].StorageConfig?.Data?.PreferredPoolKind - : undefined, - ), + sortAccessor: (row) => + row.Family + ? families[row.Family].StorageConfig?.Data?.PreferredPoolKind + : undefined, }, { name: SchemaViewerColumns.columnCodec, width: 100, render: ({row}) => row.Family ? formatColumnCodec(families[row.Family].ColumnCodec) : undefined, - sortAscending: ({row: rowA}, {row: rowB}) => - comparePossiblyEmptyStrings( - rowA.Family ? families[rowA.Family].ColumnCodec : undefined, - rowB.Family ? families[rowB.Family].ColumnCodec : undefined, - ), + sortAccessor: (row) => (row.Family ? families[row.Family].ColumnCodec : undefined), }, ); } From 83528e07fc7dea477f69de32e5fb3cb92fd51319 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Thu, 2 May 2024 12:12:00 +0200 Subject: [PATCH 12/15] style: move some functions to `helpers.ts` --- .../Schema/SchemaViewer/SchemaViewer.tsx | 43 +++---------------- .../Tenant/Schema/SchemaViewer/helpers.ts | 35 +++++++++++++++ 2 files changed, 41 insertions(+), 37 deletions(-) create mode 100644 src/containers/Tenant/Schema/SchemaViewer/helpers.ts diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index f7d528b1bd..2f4beaf810 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -5,18 +5,14 @@ import type {Column} from '@gravity-ui/react-data-table'; import {Icon} from '../../../../components/Icon'; import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; -import {EColumnCodec} from '../../../../types/api/schema'; -import type { - EPathType, - TColumnDescription, - TColumnTableDescription, - TFamilyDescription, -} from '../../../../types/api/schema'; +import type {EPathType, TColumnDescription, TFamilyDescription} from '../../../../types/api/schema'; import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; import {useTypedSelector} from '../../../../utils/hooks'; import {isColumnEntityType, isExternalTable, isRowTable, isTableType} from '../../utils/schema'; +import {formatColumnCodec, prepareOlapTableSchema} from './helpers'; + import './SchemaViewer.scss'; const b = cn('schema-viewer'); @@ -39,35 +35,6 @@ interface SchemaViewerProps { withFamilies?: boolean; } -function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { - const {Name, Schema} = tableSchema; - - if (Schema) { - const {Columns, KeyColumnNames} = Schema; - const KeyColumnIds = KeyColumnNames?.map((name: string) => { - const column = Columns?.find((el) => el.Name === name); - return column?.Id; - }).filter((id): id is number => id !== undefined); - - return { - Columns, - KeyColumnNames, - Name, - KeyColumnIds, - }; - } - - return { - Name, - }; -} - -function formatColumnCodec(codec?: EColumnCodec) { - if (!codec) return null; - if (codec === EColumnCodec.ColumnCodecPlain) return 'None'; - return codec.replace('ColumnCodec', '').toLocaleLowerCase(); -} - export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewerProps) => { const {data, loading} = useTypedSelector((state) => state.schema); const currentObjectData = path ? data[path] : undefined; @@ -91,7 +58,9 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer currentObjectData?.PathDescription?.Table?.PartitionConfig?.ColumnFamilies?.reduce< Record >((acc, family) => { - if (family.Id) acc[family.Id] = family; + if (family.Id) { + acc[family.Id] = family; + } return acc; }, {}) ?? {}; diff --git a/src/containers/Tenant/Schema/SchemaViewer/helpers.ts b/src/containers/Tenant/Schema/SchemaViewer/helpers.ts new file mode 100644 index 0000000000..0eb4248384 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/helpers.ts @@ -0,0 +1,35 @@ +import type {TColumnTableDescription} from '../../../../types/api/schema'; +import {EColumnCodec} from '../../../../types/api/schema'; + +export function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { + const {Name, Schema} = tableSchema; + + if (Schema) { + const {Columns, KeyColumnNames} = Schema; + const KeyColumnIds = KeyColumnNames?.map((name: string) => { + const column = Columns?.find((el) => el.Name === name); + return column?.Id; + }).filter((id): id is number => id !== undefined); + + return { + Columns, + KeyColumnNames, + Name, + KeyColumnIds, + }; + } + + return { + Name, + }; +} + +export function formatColumnCodec(codec?: EColumnCodec) { + if (!codec) { + return null; + } + if (codec === EColumnCodec.ColumnCodecPlain) { + return 'None'; + } + return codec.replace('ColumnCodec', '').toLocaleLowerCase(); +} From d1d16596e1552e2cc657a19f20189f74a0d758dc Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Thu, 2 May 2024 12:19:52 +0200 Subject: [PATCH 13/15] fix: remove unnecessary `useMemo` --- .../Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 2f4beaf810..067cf5bde3 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -65,14 +65,15 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer }, {}) ?? {}; // Keys should be displayd by their order in keyColumnIds (Primary Key) - const keyColumnsOrderValues = React.useMemo(() => { - return keyColumnIds.reduce>((result, keyColumnId, index) => { + const keyColumnsOrderValues = keyColumnIds.reduce>( + (result, keyColumnId, index) => { // Put columns with negative values, so they will be the first with ascending sort // Minus keyColumnIds.length for the first key, -1 for the last result[keyColumnId] = index - keyColumnIds.length; return result; - }, {}); - }, [keyColumnIds]); + }, + {}, + ); const dataTableColumns: Column[] = [ { From cb93195e350d90c548e2a1f566bb180a1d5fb552 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Thu, 2 May 2024 12:25:56 +0200 Subject: [PATCH 14/15] style: remove unused import --- src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 067cf5bde3..ebe19d3435 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -1,5 +1,3 @@ -import React from 'react'; - import DataTable from '@gravity-ui/react-data-table'; import type {Column} from '@gravity-ui/react-data-table'; From 0416efb2b1a9c08f5511e5e4a1770580ac10bc56 Mon Sep 17 00:00:00 2001 From: Vladimir Lewandowski Date: Fri, 3 May 2024 13:37:40 +0200 Subject: [PATCH 15/15] style: move logic to helpers --- .../Schema/SchemaViewer/SchemaViewer.tsx | 150 ++----------- .../Tenant/Schema/SchemaViewer/helpers.ts | 35 ---- .../Tenant/Schema/SchemaViewer/helpers.tsx | 198 ++++++++++++++++++ 3 files changed, 215 insertions(+), 168 deletions(-) delete mode 100644 src/containers/Tenant/Schema/SchemaViewer/helpers.ts create mode 100644 src/containers/Tenant/Schema/SchemaViewer/helpers.tsx diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index ebe19d3435..98d3c59414 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -1,31 +1,22 @@ import DataTable from '@gravity-ui/react-data-table'; -import type {Column} from '@gravity-ui/react-data-table'; -import {Icon} from '../../../../components/Icon'; import {TableSkeleton} from '../../../../components/TableSkeleton/TableSkeleton'; -import type {EPathType, TColumnDescription, TFamilyDescription} from '../../../../types/api/schema'; +import type {EPathType} from '../../../../types/api/schema'; import {cn} from '../../../../utils/cn'; import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants'; import {useTypedSelector} from '../../../../utils/hooks'; -import {isColumnEntityType, isExternalTable, isRowTable, isTableType} from '../../utils/schema'; -import {formatColumnCodec, prepareOlapTableSchema} from './helpers'; +import { + SchemaViewerColumns, + prepareColumnDescriptions, + prepareFamilies, + prepareSchemaTableColumns, +} from './helpers'; import './SchemaViewer.scss'; const b = cn('schema-viewer'); -const SchemaViewerColumns = { - id: 'Id', - name: 'Name', - key: 'Key', - type: 'Type', - notNull: 'NotNull', - familyName: 'Family', - preferredPoolKind: 'Media', - columnCodec: 'Compression', -}; - interface SchemaViewerProps { className?: string; type?: EPathType; @@ -33,125 +24,12 @@ interface SchemaViewerProps { withFamilies?: boolean; } -export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewerProps) => { +export const SchemaViewer = ({className, type, path, withFamilies = false}: SchemaViewerProps) => { const {data, loading} = useTypedSelector((state) => state.schema); const currentObjectData = path ? data[path] : undefined; - let keyColumnIds: number[] = []; - let columns: TColumnDescription[] = []; - - if (isTableType(type) && isColumnEntityType(type)) { - const description = currentObjectData?.PathDescription?.ColumnTableDescription; - const columnTableSchema = prepareOlapTableSchema(description); - keyColumnIds = columnTableSchema.KeyColumnIds ?? []; - columns = columnTableSchema.Columns ?? []; - } else if (isExternalTable(type)) { - columns = currentObjectData?.PathDescription?.ExternalTableDescription?.Columns ?? []; - } else { - keyColumnIds = currentObjectData?.PathDescription?.Table?.KeyColumnIds ?? []; - columns = currentObjectData?.PathDescription?.Table?.Columns ?? []; - } - - const families = - currentObjectData?.PathDescription?.Table?.PartitionConfig?.ColumnFamilies?.reduce< - Record - >((acc, family) => { - if (family.Id) { - acc[family.Id] = family; - } - return acc; - }, {}) ?? {}; - - // Keys should be displayd by their order in keyColumnIds (Primary Key) - const keyColumnsOrderValues = keyColumnIds.reduce>( - (result, keyColumnId, index) => { - // Put columns with negative values, so they will be the first with ascending sort - // Minus keyColumnIds.length for the first key, -1 for the last - result[keyColumnId] = index - keyColumnIds.length; - return result; - }, - {}, - ); - - const dataTableColumns: Column[] = [ - { - name: SchemaViewerColumns.id, - width: 40, - }, - ]; - - if (!isExternalTable(type)) { - // External tables don't have key columns - dataTableColumns.push({ - name: SchemaViewerColumns.key, - width: 40, - // Table should start with key columns on sort click - defaultOrder: DataTable.ASCENDING, - // Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns - sortAccessor: (row) => (row.Id && keyColumnsOrderValues[row.Id]) || 1, - render: ({row}) => { - return row.Id && keyColumnIds.includes(row.Id) ? ( -
- -
- ) : null; - }, - }); - } - - dataTableColumns.push( - { - name: SchemaViewerColumns.name, - width: 100, - }, - { - name: SchemaViewerColumns.type, - width: 100, - }, - { - name: SchemaViewerColumns.notNull, - width: 100, - // Table should start with notNull columns on sort click - defaultOrder: DataTable.DESCENDING, - render: ({row}) => { - if (row.NotNull) { - return '\u2713'; - } - - return undefined; - }, - }, - ); - - if (withFamilies && isRowTable(type)) { - dataTableColumns.push( - { - name: SchemaViewerColumns.familyName, - width: 100, - render: ({row}) => (row.Family ? families[row.Family].Name : undefined), - sortAccessor: (row) => (row.Family ? families[row.Family].Name : undefined), - }, - { - name: SchemaViewerColumns.preferredPoolKind, - width: 100, - render: ({row}) => - row.Family - ? families[row.Family].StorageConfig?.Data?.PreferredPoolKind - : undefined, - sortAccessor: (row) => - row.Family - ? families[row.Family].StorageConfig?.Data?.PreferredPoolKind - : undefined, - }, - { - name: SchemaViewerColumns.columnCodec, - width: 100, - render: ({row}) => - row.Family ? formatColumnCodec(families[row.Family].ColumnCodec) : undefined, - sortAccessor: (row) => (row.Family ? families[row.Family].ColumnCodec : undefined), - }, - ); - } + const {columns, keyColumnIds} = prepareColumnDescriptions(type, currentObjectData); + const families = prepareFamilies(currentObjectData); return (
@@ -161,7 +39,13 @@ export const SchemaViewer = ({className, type, path, withFamilies}: SchemaViewer { - const column = Columns?.find((el) => el.Name === name); - return column?.Id; - }).filter((id): id is number => id !== undefined); - - return { - Columns, - KeyColumnNames, - Name, - KeyColumnIds, - }; - } - - return { - Name, - }; -} - -export function formatColumnCodec(codec?: EColumnCodec) { - if (!codec) { - return null; - } - if (codec === EColumnCodec.ColumnCodecPlain) { - return 'None'; - } - return codec.replace('ColumnCodec', '').toLocaleLowerCase(); -} diff --git a/src/containers/Tenant/Schema/SchemaViewer/helpers.tsx b/src/containers/Tenant/Schema/SchemaViewer/helpers.tsx new file mode 100644 index 0000000000..ed8fcf7781 --- /dev/null +++ b/src/containers/Tenant/Schema/SchemaViewer/helpers.tsx @@ -0,0 +1,198 @@ +import type {ClassNameFormatter} from '@bem-react/classname'; +import DataTable from '@gravity-ui/react-data-table'; +import type {Column} from '@gravity-ui/react-data-table'; + +import {Icon} from '../../../../components/Icon'; +import type { + EPathType, + TColumnDescription, + TColumnTableDescription, + TEvDescribeSchemeResult, + TFamilyDescription, +} from '../../../../types/api/schema'; +import {EColumnCodec} from '../../../../types/api/schema'; +import {isColumnEntityType, isExternalTable, isRowTable, isTableType} from '../../utils/schema'; + +export const SchemaViewerColumns = { + id: 'Id', + name: 'Name', + key: 'Key', + type: 'Type', + notNull: 'NotNull', + familyName: 'Family', + preferredPoolKind: 'Media', + columnCodec: 'Compression', +}; + +function prepareOlapTableSchema(tableSchema: TColumnTableDescription = {}) { + const {Name, Schema} = tableSchema; + + if (Schema) { + const {Columns, KeyColumnNames} = Schema; + const KeyColumnIds = KeyColumnNames?.map((name: string) => { + const column = Columns?.find((el) => el.Name === name); + return column?.Id; + }).filter((id): id is number => id !== undefined); + + return { + Columns, + KeyColumnNames, + Name, + KeyColumnIds, + }; + } + + return { + Name, + }; +} + +function formatColumnCodec(codec?: EColumnCodec) { + if (!codec) { + return null; + } + if (codec === EColumnCodec.ColumnCodecPlain) { + return 'None'; + } + return codec.replace('ColumnCodec', '').toLocaleLowerCase(); +} + +export function prepareColumnDescriptions( + type?: EPathType, + scheme?: TEvDescribeSchemeResult, +): {columns: TColumnDescription[]; keyColumnIds: number[]} { + let keyColumnIds: number[] = []; + let columns: TColumnDescription[] = []; + + if (isTableType(type) && isColumnEntityType(type)) { + const description = scheme?.PathDescription?.ColumnTableDescription; + const columnTableSchema = prepareOlapTableSchema(description); + keyColumnIds = columnTableSchema.KeyColumnIds ?? []; + columns = columnTableSchema.Columns ?? []; + } else if (isExternalTable(type)) { + columns = scheme?.PathDescription?.ExternalTableDescription?.Columns ?? []; + } else { + keyColumnIds = scheme?.PathDescription?.Table?.KeyColumnIds ?? []; + columns = scheme?.PathDescription?.Table?.Columns ?? []; + } + + return {columns, keyColumnIds}; +} + +export function prepareFamilies( + scheme?: TEvDescribeSchemeResult, +): Record { + return ( + scheme?.PathDescription?.Table?.PartitionConfig?.ColumnFamilies?.reduce< + Record + >((acc, family) => { + if (family.Id) { + acc[family.Id] = family; + } + return acc; + }, {}) ?? {} + ); +} + +export function prepareSchemaTableColumns(options: { + type?: EPathType; + b: ClassNameFormatter; + families: Record; + keyColumnIds: number[]; + withFamilies: boolean; +}): Column[] { + const keyColumnsOrderValues = options.keyColumnIds.reduce>( + (result, keyColumnId, index) => { + // Put columns with negative values, so they will be the first with ascending sort + // Minus keyColumnIds.length for the first key, -1 for the last + result[keyColumnId] = index - options.keyColumnIds.length; + return result; + }, + {}, + ); + + const columns: Column[] = [ + { + name: SchemaViewerColumns.id, + width: 40, + }, + ]; + + if (!isExternalTable(options.type)) { + // External tables don't have key columns + columns.push({ + name: SchemaViewerColumns.key, + width: 40, + // Table should start with key columns on sort click + defaultOrder: DataTable.ASCENDING, + // Values in keyColumnsOrderValues are always negative, so it will be 1 for not key columns + sortAccessor: (row) => (row.Id && keyColumnsOrderValues[row.Id]) || 1, + render: ({row}) => { + return row.Id && options.keyColumnIds.includes(row.Id) ? ( +
+ +
+ ) : null; + }, + }); + } + + columns.push( + { + name: SchemaViewerColumns.name, + width: 100, + }, + { + name: SchemaViewerColumns.type, + width: 100, + }, + { + name: SchemaViewerColumns.notNull, + width: 100, + // Table should start with notNull columns on sort click + defaultOrder: DataTable.DESCENDING, + render: ({row}) => { + if (row.NotNull) { + return '\u2713'; + } + + return undefined; + }, + }, + ); + + if (options.withFamilies && isRowTable(options.type)) { + columns.push( + { + name: SchemaViewerColumns.familyName, + width: 100, + render: ({row}) => (row.Family ? options.families[row.Family].Name : undefined), + sortAccessor: (row) => (row.Family ? options.families[row.Family].Name : undefined), + }, + { + name: SchemaViewerColumns.preferredPoolKind, + width: 100, + render: ({row}) => + row.Family + ? options.families[row.Family].StorageConfig?.Data?.PreferredPoolKind + : undefined, + sortAccessor: (row) => + row.Family + ? options.families[row.Family].StorageConfig?.Data?.PreferredPoolKind + : undefined, + }, + { + name: SchemaViewerColumns.columnCodec, + width: 100, + render: ({row}) => + row.Family + ? formatColumnCodec(options.families[row.Family].ColumnCodec) + : undefined, + sortAccessor: (row) => + row.Family ? options.families[row.Family].ColumnCodec : undefined, + }, + ); + } + + return columns; +}