import React from 'react'; import type {LabelProps} from '@gravity-ui/uikit'; import {useLocation} from 'react-router-dom'; import {getTenantPath, parseQuery} from '../../../routes'; import {TENANT_DIAGNOSTICS_TABS_IDS, TENANT_PAGE} from '../../../store/reducers/tenant/constants'; import {tenantPageSchema} from '../../../store/reducers/tenant/types'; import type {TenantDiagnosticsTab} from '../../../store/reducers/tenant/types'; import {EPathSubType, EPathType} from '../../../types/api/schema'; import type {ETenantType} from '../../../types/api/tenant'; import type {TenantQuery} from '../TenantPages'; import {TenantTabsGroups} from '../TenantPages'; import { V2_DATABASE_PAGE_DIAGNOSTICS_TABS, getTenantPageForDiagnosticsTab, } from '../utils/diagnosticsNavigation'; import {useNavigationV2Enabled} from '../utils/useNavigationV2Enabled'; import i18n from './i18n'; interface Badge { text: string; theme?: LabelProps['theme']; size?: LabelProps['size']; } export type Page = { id: TenantDiagnosticsTab; title: string; badge?: Badge; }; export type DatabasePagesDisplay = 'all' | 'database' | 'diagnostics'; interface GetPagesOptions { hasTopicData?: boolean; isDatabase?: boolean; hasStorageUsage?: boolean; hasBackups?: boolean; hasConfigs?: boolean; hasAccess?: boolean; hasMonitoring?: boolean; databaseType?: ETenantType; databasePagesDisplay?: DatabasePagesDisplay; } const database = { id: TENANT_DIAGNOSTICS_TABS_IDS.database, title: i18n('title_database-overview'), }; const overview = { id: TENANT_DIAGNOSTICS_TABS_IDS.overview, title: i18n('title_info'), }; const schema = { id: TENANT_DIAGNOSTICS_TABS_IDS.schema, title: i18n('title_schema'), }; const topQueries = { id: TENANT_DIAGNOSTICS_TABS_IDS.topQueries, title: i18n('title_queries'), }; const topShards = { id: TENANT_DIAGNOSTICS_TABS_IDS.topShards, title: i18n('title_top-shards'), }; const access = { id: TENANT_DIAGNOSTICS_TABS_IDS.access, title: i18n('title_access'), }; const backups = { id: TENANT_DIAGNOSTICS_TABS_IDS.backups, title: i18n('title_backups'), }; const nodes = { id: TENANT_DIAGNOSTICS_TABS_IDS.nodes, title: i18n('title_nodes'), }; const tablets = { id: TENANT_DIAGNOSTICS_TABS_IDS.tablets, title: i18n('title_tablets'), }; const storageUsage = { id: TENANT_DIAGNOSTICS_TABS_IDS.storageUsage, title: i18n('title_storage-usage'), }; const storage = { id: TENANT_DIAGNOSTICS_TABS_IDS.storage, title: i18n('title_storage'), }; const network = { id: TENANT_DIAGNOSTICS_TABS_IDS.network, title: i18n('title_network'), }; const describe = { id: TENANT_DIAGNOSTICS_TABS_IDS.describe, title: i18n('title_describe'), }; const hotKeys = { id: TENANT_DIAGNOSTICS_TABS_IDS.hotKeys, title: i18n('title_hot-keys'), }; const graph = { id: TENANT_DIAGNOSTICS_TABS_IDS.graph, title: i18n('title_graph'), }; const computationGraph = { id: TENANT_DIAGNOSTICS_TABS_IDS.computationGraph, title: i18n('title_computation-graph'), }; const consumers = { id: TENANT_DIAGNOSTICS_TABS_IDS.consumers, title: i18n('title_consumers'), }; const partitions = { id: TENANT_DIAGNOSTICS_TABS_IDS.partitions, title: i18n('title_partitions'), }; const topicData = { id: TENANT_DIAGNOSTICS_TABS_IDS.topicData, title: i18n('title_data'), }; const configs = { id: TENANT_DIAGNOSTICS_TABS_IDS.configs, title: i18n('title_configs'), }; const operations = { id: TENANT_DIAGNOSTICS_TABS_IDS.operations, title: i18n('title_operations'), }; const monitoring = { id: TENANT_DIAGNOSTICS_TABS_IDS.monitoring, title: i18n('title_monitoring'), }; const ASYNC_REPLICATION_PAGES = [overview, tablets, describe, access]; const TRANSFER_PAGES = [overview, tablets, describe, access]; const ALL_DB_PAGES = [ overview, monitoring, topQueries, topShards, nodes, tablets, storage, network, describe, configs, access, operations, backups, ]; const databasePageById = { [TENANT_DIAGNOSTICS_TABS_IDS.database]: database, [TENANT_DIAGNOSTICS_TABS_IDS.monitoring]: monitoring, [TENANT_DIAGNOSTICS_TABS_IDS.topQueries]: topQueries, [TENANT_DIAGNOSTICS_TABS_IDS.nodes]: nodes, [TENANT_DIAGNOSTICS_TABS_IDS.tablets]: tablets, [TENANT_DIAGNOSTICS_TABS_IDS.storage]: storage, [TENANT_DIAGNOSTICS_TABS_IDS.network]: network, [TENANT_DIAGNOSTICS_TABS_IDS.configs]: configs, [TENANT_DIAGNOSTICS_TABS_IDS.operations]: operations, [TENANT_DIAGNOSTICS_TABS_IDS.backups]: backups, } satisfies Record<(typeof V2_DATABASE_PAGE_DIAGNOSTICS_TABS)[number], Page>; const DB_PAGES = V2_DATABASE_PAGE_DIAGNOSTICS_TABS.map((id) => databasePageById[id]); const DIAGNOSTICS_DB_PAGES = [overview, topShards, nodes, tablets, describe, access]; const ALL_SERVERLESS_DB_PAGES = [ overview, monitoring, topQueries, topShards, tablets, describe, configs, access, operations, backups, ]; const SERVERLESS_DB_PAGES = [ database, monitoring, topQueries, tablets, configs, operations, backups, ]; const DIAGNOSTICS_SERVERLESS_DB_PAGES = [overview, topShards, tablets, describe, access]; const TABLE_PAGES = [ overview, schema, topShards, nodes, graph, tablets, storageUsage, hotKeys, describe, access, ]; const COLUMN_TABLE_PAGES = [ overview, schema, topShards, nodes, tablets, storageUsage, describe, access, ]; const SYSTEM_VIEW_PAGES = [overview, schema, nodes, describe, access]; const DIR_PAGES = [overview, topShards, nodes, describe, access]; const CDC_STREAM_PAGES = [overview, consumers, partitions, topicData, nodes, describe, access]; const CDC_STREAM_IMPL_PAGES = [overview, nodes, tablets, describe, access]; const TOPIC_PAGES = [overview, consumers, partitions, topicData, nodes, tablets, describe, access]; const EXTERNAL_DATA_SOURCE_PAGES = [overview, describe, access]; const EXTERNAL_TABLE_PAGES = [overview, schema, describe, access]; const VIEW_PAGES = [overview, schema, describe, access]; const STREAMING_QUERY_PAGES = [overview, computationGraph, describe, access]; const SECRET_PAGES = [overview, describe, access]; // verbose mapping to guarantee correct tabs for new path types // TS will error when a new type is added but not mapped here const pathTypeToPages: Record = { [EPathType.EPathTypeInvalid]: undefined, [EPathType.EPathTypeSubDomain]: ALL_DB_PAGES, [EPathType.EPathTypeExtSubDomain]: ALL_DB_PAGES, [EPathType.EPathTypeColumnStore]: ALL_DB_PAGES, [EPathType.EPathTypeTable]: TABLE_PAGES, [EPathType.EPathTypeColumnTable]: COLUMN_TABLE_PAGES, [EPathType.EPathTypeSysView]: SYSTEM_VIEW_PAGES, [EPathType.EPathTypeDir]: DIR_PAGES, [EPathType.EPathTypeTableIndex]: DIR_PAGES, [EPathType.EPathTypeCdcStream]: CDC_STREAM_PAGES, [EPathType.EPathTypePersQueueGroup]: TOPIC_PAGES, [EPathType.EPathTypeExternalDataSource]: EXTERNAL_DATA_SOURCE_PAGES, [EPathType.EPathTypeExternalTable]: EXTERNAL_TABLE_PAGES, [EPathType.EPathTypeStreamingQuery]: STREAMING_QUERY_PAGES, [EPathType.EPathTypeView]: VIEW_PAGES, [EPathType.EPathTypeReplication]: ASYNC_REPLICATION_PAGES, [EPathType.EPathTypeTransfer]: TRANSFER_PAGES, [EPathType.EPathTypeResourcePool]: DIR_PAGES, [EPathType.EPathTypeSecret]: SECRET_PAGES, }; const pathSubTypeToPages: Record = { [EPathSubType.EPathSubTypeStreamImpl]: CDC_STREAM_IMPL_PAGES, [EPathSubType.EPathSubTypeSyncIndexImplTable]: undefined, [EPathSubType.EPathSubTypeAsyncIndexImplTable]: undefined, [EPathSubType.EPathSubTypeVectorKmeansTreeIndexImplTable]: undefined, [EPathSubType.EPathSubTypeFulltextIndexImplTable]: undefined, [EPathSubType.EPathSubTypeLocalMinMaxIndex]: undefined, [EPathSubType.EPathSubTypeEmpty]: undefined, }; function computeInitialPages(type?: EPathType, subType?: EPathSubType) { const subTypePages = subType ? pathSubTypeToPages[subType] : undefined; const typePages = type ? pathTypeToPages[type] : undefined; return subTypePages || typePages || DIR_PAGES; } function getDatabasePages(databaseType?: ETenantType, databasePagesDisplay?: DatabasePagesDisplay) { if (databaseType === 'Serverless') { if (databasePagesDisplay === 'diagnostics') { return DIAGNOSTICS_SERVERLESS_DB_PAGES; } if (databasePagesDisplay === 'database') { return SERVERLESS_DB_PAGES; } return ALL_SERVERLESS_DB_PAGES; } if (databasePagesDisplay === 'diagnostics') { return DIAGNOSTICS_DB_PAGES; } if (databasePagesDisplay === 'database') { return DB_PAGES; } return ALL_DB_PAGES; } function applyFilters(pages: Page[], options: GetPagesOptions = {}) { const result = pages; const removals: TenantDiagnosticsTab[] = []; if (!options.hasTopicData) { removals.push(TENANT_DIAGNOSTICS_TABS_IDS.topicData); } if (!options.hasStorageUsage) { removals.push(TENANT_DIAGNOSTICS_TABS_IDS.storageUsage); } if (!options.hasBackups) { removals.push(TENANT_DIAGNOSTICS_TABS_IDS.backups); } if (!options.hasConfigs) { removals.push(TENANT_DIAGNOSTICS_TABS_IDS.configs); } if (!options.hasAccess) { removals.push(TENANT_DIAGNOSTICS_TABS_IDS.access); } if (!options.hasMonitoring) { removals.push(TENANT_DIAGNOSTICS_TABS_IDS.monitoring); } return result.filter((p) => !removals.includes(p.id)); } export const getPagesByType = ( type?: EPathType, subType?: EPathSubType, options?: GetPagesOptions, ) => { const base = computeInitialPages(type, subType); const seeded = options?.isDatabase ? getDatabasePages(options?.databaseType, options?.databasePagesDisplay) : base; return applyFilters(seeded, options); }; export const useDiagnosticsPageLinkGetter = () => { const location = useLocation(); const queryParams = parseQuery(location); const isV2Enabled = useNavigationV2Enabled(); const getLink = React.useCallback( (tab: TenantDiagnosticsTab, params?: TenantQuery) => { return getTenantPath({ ...queryParams, ...params, // Make sure the link points to the page that actually owns this tab // (database vs diagnostics in v2; always diagnostics in v1). Without // this, switching only `diagnosticsTab` either does nothing (v2, // staying on the database overview) or lands on a page where the // tab does not exist (and gets silently replaced by the first tab). // Spread after `params` so callers cannot accidentally override the // page/tab the helper is responsible for. [TENANT_PAGE]: getTenantPageForDiagnosticsTab( tab, isV2Enabled, tenantPageSchema.safeParse(queryParams[TENANT_PAGE]).data, ), [TenantTabsGroups.diagnosticsTab]: tab, }); }, [queryParams, isV2Enabled], ); return getLink; };