diff --git a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts index ead594afe2..87a4de5a4a 100644 --- a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts +++ b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts @@ -141,6 +141,7 @@ const SERVERLESS_DATABASE_PAGES = [ const TABLE_PAGES = [overview, schema, topShards, nodes, graph, tablets, hotKeys, describe, access]; const COLUMN_TABLE_PAGES = [overview, schema, topShards, nodes, tablets, describe, access]; +const SYSTEM_VIEW_PAGES = [overview, schema, nodes, describe, access]; const DIR_PAGES = [overview, topShards, nodes, describe, access]; @@ -164,6 +165,7 @@ const pathTypeToPages: Record = { [EPathType.EPathTypeTable]: TABLE_PAGES, [EPathType.EPathTypeColumnTable]: COLUMN_TABLE_PAGES, + [EPathType.EPathTypeSysView]: SYSTEM_VIEW_PAGES, [EPathType.EPathTypeDir]: DIR_PAGES, [EPathType.EPathTypeTableIndex]: DIR_PAGES, diff --git a/src/containers/Tenant/Diagnostics/Overview/Overview.tsx b/src/containers/Tenant/Diagnostics/Overview/Overview.tsx index 812878a749..488ceb886f 100644 --- a/src/containers/Tenant/Diagnostics/Overview/Overview.tsx +++ b/src/containers/Tenant/Diagnostics/Overview/Overview.tsx @@ -8,6 +8,7 @@ import {EPathType} from '../../../../types/api/schema'; import {useAutoRefreshInterval} from '../../../../utils/hooks'; import {ExternalDataSourceInfo} from '../../Info/ExternalDataSource/ExternalDataSource'; import {ExternalTableInfo} from '../../Info/ExternalTable/ExternalTable'; +import {SystemViewInfo} from '../../Info/SystemView/SystemView'; import {ViewInfo} from '../../Info/View/View'; import {AsyncReplicationInfo} from './AsyncReplicationInfo'; @@ -42,6 +43,7 @@ function Overview({type, path, database, databaseFullPath}: OverviewProps) { [EPathType.EPathTypeDir]: undefined, [EPathType.EPathTypeResourcePool]: undefined, [EPathType.EPathTypeTable]: undefined, + [EPathType.EPathTypeSysView]: () => , [EPathType.EPathTypeSubDomain]: undefined, [EPathType.EPathTypeTableIndex]: () => , [EPathType.EPathTypeExtSubDomain]: undefined, diff --git a/src/containers/Tenant/Info/SystemView/SystemView.tsx b/src/containers/Tenant/Info/SystemView/SystemView.tsx new file mode 100644 index 0000000000..7a73fc943b --- /dev/null +++ b/src/containers/Tenant/Info/SystemView/SystemView.tsx @@ -0,0 +1,39 @@ +import {Text} from '@gravity-ui/uikit'; + +import type {YDBDefinitionListItem} from '../../../../components/YDBDefinitionList/YDBDefinitionList'; +import {YDBDefinitionList} from '../../../../components/YDBDefinitionList/YDBDefinitionList'; +import type {TEvDescribeSchemeResult} from '../../../../types/api/schema'; +import {prepareSystemViewType} from '../../../../utils/schema'; +import {getEntityName} from '../../utils'; +import i18n from '../i18n'; + +const prepareSystemViewItems = (data: TEvDescribeSchemeResult): YDBDefinitionListItem[] => { + const systemViewType = data.PathDescription?.SysViewDescription?.Type; + + return [ + { + name: i18n('field_system-view-type'), + content: prepareSystemViewType(systemViewType), + }, + ]; +}; + +interface SystemViewInfoProps { + data?: TEvDescribeSchemeResult; +} + +export function SystemViewInfo({data}: SystemViewInfoProps) { + const entityName = getEntityName(data?.PathDescription); + + if (!data) { + return ( + + {i18n('no-entity-data', {entityName})} + + ); + } + + const items = prepareSystemViewItems(data); + + return ; +} diff --git a/src/containers/Tenant/Info/i18n/en.json b/src/containers/Tenant/Info/i18n/en.json index f25d7a013b..2b0392cedd 100644 --- a/src/containers/Tenant/Info/i18n/en.json +++ b/src/containers/Tenant/Info/i18n/en.json @@ -6,5 +6,9 @@ "external-objects.auth-method.none": "None", "external-objects.auth-method.service-account": "Service Account", - "view.query-text": "Query Text" + "view.query-text": "Query Text", + + "field_system-view-type": "System view type", + + "no-entity-data": "No {{entityName}} data" } diff --git a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx index 17c1a9e92d..de70290c9e 100644 --- a/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx +++ b/src/containers/Tenant/ObjectSummary/ObjectSummary.tsx @@ -32,6 +32,7 @@ import { formatSecondsToHours, } from '../../../utils/dataFormatters/dataFormatters'; import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks'; +import {prepareSystemViewType} from '../../../utils/schema'; import {EntityTitle} from '../EntityTitle/EntityTitle'; import {SchemaViewer} from '../Schema/SchemaViewer/SchemaViewer'; import {useCurrentSchema} from '../TenantContext'; @@ -233,6 +234,12 @@ export function ObjectSummary({ content: PathDescription?.TablePartitions?.length, }, ], + [EPathType.EPathTypeSysView]: () => [ + { + name: i18n('field_system-view-type'), + content: prepareSystemViewType(PathDescription?.SysViewDescription?.Type), + }, + ], [EPathType.EPathTypeSubDomain]: getDatabaseOverview, [EPathType.EPathTypeTableIndex]: undefined, [EPathType.EPathTypeExtSubDomain]: getDatabaseOverview, diff --git a/src/containers/Tenant/ObjectSummary/i18n/en.json b/src/containers/Tenant/ObjectSummary/i18n/en.json index 7dcb277cf8..89cd116f5b 100644 --- a/src/containers/Tenant/ObjectSummary/i18n/en.json +++ b/src/containers/Tenant/ObjectSummary/i18n/en.json @@ -12,6 +12,7 @@ "field_data-size": "Data size", "field_row-count": "Row count", "field_partitions": "Partitions count", + "field_system-view-type": "System view type", "field_paths": "Paths", "field_shards": "Shards", "field_state": "State", diff --git a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx index 267367ae13..fe3d4e649f 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/SchemaViewer.tsx @@ -11,6 +11,7 @@ import { isColumnEntityType, isExternalTableType, isRowTableType, + isSystemViewType, isViewType, } from '../../utils/schema'; @@ -20,6 +21,7 @@ import { getColumnTableColumns, getExternalTableColumns, getRowTableColumns, + getSystemViewColumns, getViewColumns, } from './columns'; import {prepareSchemaData, prepareViewSchema} from './prepareData'; @@ -82,6 +84,9 @@ export const SchemaViewer = ({ if (isViewType(type)) { return getViewColumns(tableData); } + if (isSystemViewType(type)) { + return getSystemViewColumns(tableData); + } if (isExternalTableType(type)) { return getExternalTableColumns(tableData); } diff --git a/src/containers/Tenant/Schema/SchemaViewer/columns.tsx b/src/containers/Tenant/Schema/SchemaViewer/columns.tsx index 7b403c6207..37fa74c3ce 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/columns.tsx +++ b/src/containers/Tenant/Schema/SchemaViewer/columns.tsx @@ -146,6 +146,9 @@ function normalizeColumns(columns: SchemaColumn[], data?: SchemaData[]) { export function getViewColumns(data?: SchemaData[]): SchemaColumn[] { return normalizeColumns([nameColumn, typeColumn], data); } +export function getSystemViewColumns(data?: SchemaData[]): SchemaColumn[] { + return normalizeColumns([idColumn, nameColumn, typeColumn, notNullColumn], data); +} export function getExternalTableColumns(data?: SchemaData[]): SchemaColumn[] { return normalizeColumns([idColumn, nameColumn, typeColumn, notNullColumn], data); } diff --git a/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts b/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts index 93607742f4..dd85562e95 100644 --- a/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts +++ b/src/containers/Tenant/Schema/SchemaViewer/prepareData.ts @@ -9,7 +9,12 @@ import type { } from '../../../../types/api/schema'; import {EColumnCodec} from '../../../../types/api/schema'; import type {Nullable} from '../../../../utils/typecheckers'; -import {isColumnEntityType, isExternalTableType, isRowTableType} from '../../utils/schema'; +import { + isColumnEntityType, + isExternalTableType, + isRowTableType, + isSystemViewType, +} from '../../utils/schema'; import type {SchemaData} from './types'; @@ -126,7 +131,7 @@ export function prepareSchemaData( ): SchemaData[] { const {Table, ColumnTableDescription, ExternalTableDescription} = schema?.PathDescription || {}; - if (isRowTableType(type)) { + if (isRowTableType(type) || isSystemViewType(type)) { return prepareRowTableSchema(Table); } else if (isColumnEntityType(type)) { return prepareColumnTableSchema(ColumnTableDescription); diff --git a/src/containers/Tenant/i18n/en.json b/src/containers/Tenant/i18n/en.json index 1e44ac2f75..ec666ac589 100644 --- a/src/containers/Tenant/i18n/en.json +++ b/src/containers/Tenant/i18n/en.json @@ -68,5 +68,22 @@ "label_download": "Download healthcheck data", "label_grant-access": "Grant access", - "context_grant-access": "Please note that granular rights can be combined into groups" + "context_grant-access": "Please note that granular rights can be combined into groups", + + "entity-name_database": "Database", + "entity-name_directory": "Directory", + "entity-name_table": "Table", + "entity-name_system-view": "System view", + "entity-name_secondary-index": "Secondary Index", + "entity-name_tablestore": "Tablestore", + "entity-name_column-oriented-table": "Column-oriented table", + "entity-name_changefeed": "Changefeed", + "entity-name_topic": "Topic", + "entity-name_external-data-source": "External Data Source", + "entity-name_external-table": "External Table", + "entity-name_view": "View", + "entity-name_async-replication": "Async Replication", + "entity-name_transfer": "Transfer", + "entity-name_resource-pool": "Resource Pool", + "entity-name_secondary-index-table": "Secondary Index Table" } diff --git a/src/containers/Tenant/utils/schema.ts b/src/containers/Tenant/utils/schema.ts index 726783c2d7..43eb559f9d 100644 --- a/src/containers/Tenant/utils/schema.ts +++ b/src/containers/Tenant/utils/schema.ts @@ -2,6 +2,7 @@ import type {NavigationTreeNodeType} from 'ydb-ui-components'; import {EPathSubType, EPathType} from '../../../types/api/schema'; import type {ETenantType} from '../../../types/api/tenant'; +import i18n from '../i18n'; // this file contains verbose mappings that are typed in a way that ensures // correctness when a new node type or a new path type is added @@ -25,6 +26,7 @@ const pathTypeToNodeType: Record [EPathType.EPathTypeColumnStore]: 'directory', [EPathType.EPathTypeTable]: 'table', + [EPathType.EPathTypeSysView]: 'system_table', [EPathType.EPathTypeTableIndex]: 'index', @@ -61,8 +63,8 @@ export const mapPathTypeToNavigationTreeType = ( // ==================== const pathSubTypeToEntityName: Record = { - [EPathSubType.EPathSubTypeSyncIndexImplTable]: 'Secondary Index Table', - [EPathSubType.EPathSubTypeAsyncIndexImplTable]: 'Secondary Index Table', + [EPathSubType.EPathSubTypeSyncIndexImplTable]: i18n('entity-name_secondary-index-table'), + [EPathSubType.EPathSubTypeAsyncIndexImplTable]: i18n('entity-name_secondary-index-table'), [EPathSubType.EPathSubTypeStreamImpl]: undefined, [EPathSubType.EPathSubTypeEmpty]: undefined, @@ -71,25 +73,26 @@ const pathSubTypeToEntityName: Record = { const pathTypeToEntityName: Record = { [EPathType.EPathTypeInvalid]: undefined, - [EPathType.EPathTypeSubDomain]: 'Database', - [EPathType.EPathTypeExtSubDomain]: 'Database', + [EPathType.EPathTypeSubDomain]: i18n('entity-name_database'), + [EPathType.EPathTypeExtSubDomain]: i18n('entity-name_database'), - [EPathType.EPathTypeDir]: 'Directory', - [EPathType.EPathTypeTable]: 'Table', - [EPathType.EPathTypeTableIndex]: 'Secondary Index', - [EPathType.EPathTypeColumnStore]: 'Tablestore', - [EPathType.EPathTypeColumnTable]: 'Column-oriented table', - [EPathType.EPathTypeCdcStream]: 'Changefeed', - [EPathType.EPathTypePersQueueGroup]: 'Topic', + [EPathType.EPathTypeDir]: i18n('entity-name_directory'), + [EPathType.EPathTypeTable]: i18n('entity-name_table'), + [EPathType.EPathTypeSysView]: i18n('entity-name_system-view'), + [EPathType.EPathTypeTableIndex]: i18n('entity-name_secondary-index'), + [EPathType.EPathTypeColumnStore]: i18n('entity-name_tablestore'), + [EPathType.EPathTypeColumnTable]: i18n('entity-name_column-oriented-table'), + [EPathType.EPathTypeCdcStream]: i18n('entity-name_changefeed'), + [EPathType.EPathTypePersQueueGroup]: i18n('entity-name_topic'), - [EPathType.EPathTypeExternalDataSource]: 'External Data Source', - [EPathType.EPathTypeExternalTable]: 'External Table', + [EPathType.EPathTypeExternalDataSource]: i18n('entity-name_external-data-source'), + [EPathType.EPathTypeExternalTable]: i18n('entity-name_external-table'), - [EPathType.EPathTypeView]: 'View', + [EPathType.EPathTypeView]: i18n('entity-name_view'), - [EPathType.EPathTypeReplication]: 'Async Replication', - [EPathType.EPathTypeTransfer]: 'Transfer', - [EPathType.EPathTypeResourcePool]: 'Resource Pool', + [EPathType.EPathTypeReplication]: i18n('entity-name_async-replication'), + [EPathType.EPathTypeTransfer]: i18n('entity-name_transfer'), + [EPathType.EPathTypeResourcePool]: i18n('entity-name_resource-pool'), }; export const mapPathTypeToEntityName = ( @@ -115,6 +118,7 @@ export const mapDatabaseTypeToDBName = (type?: ETenantType) => type && databaseT const pathTypeToIsTable: Record = { [EPathType.EPathTypeTable]: true, [EPathType.EPathTypeColumnTable]: true, + [EPathType.EPathTypeSysView]: true, [EPathType.EPathTypeExternalTable]: true, @@ -160,6 +164,7 @@ const pathTypeToIsColumn: Record = { [EPathType.EPathTypeInvalid]: false, [EPathType.EPathTypeDir]: false, [EPathType.EPathTypeTable]: false, + [EPathType.EPathTypeSysView]: false, [EPathType.EPathTypeSubDomain]: false, [EPathType.EPathTypeTableIndex]: false, [EPathType.EPathTypeExtSubDomain]: false, @@ -189,6 +194,7 @@ const pathTypeToIsDatabase: Record = { [EPathType.EPathTypeColumnStore]: false, [EPathType.EPathTypeColumnTable]: false, [EPathType.EPathTypeTable]: false, + [EPathType.EPathTypeSysView]: false, [EPathType.EPathTypeTableIndex]: false, [EPathType.EPathTypeCdcStream]: false, [EPathType.EPathTypePersQueueGroup]: false, @@ -240,6 +246,7 @@ const pathTypeToChildless: Record = { [EPathType.EPathTypeColumnTable]: false, [EPathType.EPathTypeDir]: false, [EPathType.EPathTypeTable]: false, + [EPathType.EPathTypeSysView]: false, [EPathType.EPathTypeSubDomain]: false, [EPathType.EPathTypeTableIndex]: false, [EPathType.EPathTypeExtSubDomain]: false, @@ -253,3 +260,4 @@ export const isChildlessPathType = (type?: EPathType, subType?: EPathSubType) => export const isExternalTableType = (type?: EPathType) => type === EPathType.EPathTypeExternalTable; export const isRowTableType = (type?: EPathType) => type === EPathType.EPathTypeTable; export const isViewType = (type?: EPathType) => type === EPathType.EPathTypeView; +export const isSystemViewType = (type?: EPathType) => type === EPathType.EPathTypeSysView; diff --git a/src/types/api/schema/schema.ts b/src/types/api/schema/schema.ts index 3e27bdca54..7175a26f82 100644 --- a/src/types/api/schema/schema.ts +++ b/src/types/api/schema/schema.ts @@ -6,6 +6,7 @@ import type {TExternalDataSourceDescription} from './externalDataSource'; import type {TExternalTableDescription} from './externalTable'; import type {TPersQueueGroupDescription} from './persQueueGroup'; import type {TReplicationDescription} from './replication'; +import type {TSysViewDescription} from './sysView'; import type {TTableDescription, TTableStats} from './table'; import type {TIndexDescription} from './tableIndex'; import type {TViewDescription} from './view'; @@ -86,6 +87,7 @@ export interface TPathDescription { ExternalDataSourceDescription?: TExternalDataSourceDescription; ViewDescription?: TViewDescription; + SysViewDescription?: TSysViewDescription; ReplicationDescription?: TReplicationDescription; } @@ -283,6 +285,7 @@ export enum EPathType { EPathTypeTable = 'EPathTypeTable', EPathTypePersQueueGroup = 'EPathTypePersQueueGroup', EPathTypeSubDomain = 'EPathTypeSubDomain', + EPathTypeSysView = 'EPathTypeSysView', EPathTypeTableIndex = 'EPathTypeTableIndex', EPathTypeExtSubDomain = 'EPathTypeExtSubDomain', diff --git a/src/types/api/schema/sysView.ts b/src/types/api/schema/sysView.ts new file mode 100644 index 0000000000..48aca606d9 --- /dev/null +++ b/src/types/api/schema/sysView.ts @@ -0,0 +1,44 @@ +import type {TPathID} from './shared'; + +export interface TSysViewDescription { + Name?: string; + Type?: ESysViewType; + SourceObject?: TPathID; +} + +export type ESysViewType = + | 'EPartitionStats' + | 'ENodes' + | 'ETopQueriesByDurationOneMinute' + | 'ETopQueriesByDurationOneHour' + | 'ETopQueriesByReadBytesOneMinute' + | 'ETopQueriesByReadBytesOneHour' + | 'ETopQueriesByCpuTimeOneMinute' + | 'ETopQueriesByCpuTimeOneHour' + | 'ETopQueriesByRequestUnitsOneMinute' + | 'ETopQueriesByRequestUnitsOneHour' + | 'EQuerySessions' + | 'EPDisks' + | 'EVSlots' + | 'EGroups' + | 'EStoragePools' + | 'EStorageStats' + | 'ETablets' + | 'EQueryMetricsOneMinute' + | 'ETopPartitionsByCpuOneMinute' + | 'ETopPartitionsByCpuOneHour' + | 'ETopPartitionsByTliOneMinute' + | 'ETopPartitionsByTliOneHour' + | 'EResourcePoolClassifiers' + | 'EResourcePools' + | 'EAuthUsers' + | 'EAuthGroups' + | 'EAuthGroupMembers' + | 'EAuthOwners' + | 'EAuthPermissions' + | 'EAuthEffectivePermissions' + | 'EPgTables' + | 'EInformationSchemaTables' + | 'EPgClass' + | 'EShowCreate' + | 'ECompileCacheQueries'; diff --git a/src/utils/schema.ts b/src/utils/schema.ts new file mode 100644 index 0000000000..b1484df77e --- /dev/null +++ b/src/utils/schema.ts @@ -0,0 +1,14 @@ +import type {ESysViewType} from '../types/api/schema/sysView'; + +export function prepareSystemViewType(type?: ESysViewType) { + if (!type) { + return undefined; + } + // System view type is enum, its format from backend is EType + // We need to display only Type, remove redundant E + // EStoragePools -> StoragePools, EStorageStats -> StorageStats + if (type.startsWith('E')) { + return type.slice(1); + } + return type; +}