diff --git a/src/containers/Tenant/Diagnostics/Describe/Describe.tsx b/src/containers/Tenant/Diagnostics/Describe/Describe.tsx index fa6fcc03b0..92eb62eb72 100644 --- a/src/containers/Tenant/Diagnostics/Describe/Describe.tsx +++ b/src/containers/Tenant/Diagnostics/Describe/Describe.tsx @@ -1,18 +1,12 @@ import {ClipboardButton} from '@gravity-ui/uikit'; -import {shallowEqual} from 'react-redux'; import {ResponseError} from '../../../../components/Errors/ResponseError'; import {JsonViewer} from '../../../../components/JsonViewer/JsonViewer'; import {useUnipikaConvert} from '../../../../components/JsonViewer/unipika/unipika'; import {Loader} from '../../../../components/Loader'; -import { - selectSchemaMergedChildrenPaths, - useGetMultiOverviewQuery, -} from '../../../../store/reducers/overview/overview'; -import type {EPathType} from '../../../../types/api/schema'; +import {overviewApi} from '../../../../store/reducers/overview/overview'; import {cn} from '../../../../utils/cn'; -import {useAutoRefreshInterval, useTypedSelector} from '../../../../utils/hooks'; -import {isEntityWithMergedImplementation} from '../../utils/schema'; +import {useAutoRefreshInterval} from '../../../../utils/hooks'; import './Describe.scss'; @@ -21,63 +15,39 @@ const b = cn('ydb-describe'); interface IDescribeProps { path: string; database: string; - type?: EPathType; } -const Describe = ({path, database, type}: IDescribeProps) => { +const Describe = ({path, database}: IDescribeProps) => { const [autoRefreshInterval] = useAutoRefreshInterval(); - const isEntityWithMergedImpl = isEntityWithMergedImplementation(type); - - const mergedChildrenPaths = useTypedSelector( - (state) => selectSchemaMergedChildrenPaths(state, path, type, database), - shallowEqual, + const {currentData, isFetching, error} = overviewApi.useGetOverviewQuery( + {path, database}, + {pollingInterval: autoRefreshInterval}, ); - let paths: string[] = []; - if (!isEntityWithMergedImpl) { - paths = [path]; - } else if (mergedChildrenPaths) { - paths = [path, ...mergedChildrenPaths]; - } - - const {mergedDescribe, loading, error} = useGetMultiOverviewQuery({ - paths, - autoRefreshInterval, - database, - }); - - let preparedDescribeData: Object | undefined; - if (mergedDescribe) { - const paths = Object.keys(mergedDescribe); - if (paths.length === 1) { - preparedDescribeData = mergedDescribe[paths[0]]; - } else { - preparedDescribeData = mergedDescribe; - } - } + const loading = isFetching && currentData === undefined; - const convertedValue = useUnipikaConvert(preparedDescribeData); + const convertedValue = useUnipikaConvert(currentData); - if (loading || (isEntityWithMergedImpl && !mergedChildrenPaths)) { + if (loading) { return ; } - if (!preparedDescribeData && !error) { + if (!currentData && !error) { return
Empty
; } return (
{error ? : null} - {preparedDescribeData ? ( + {currentData ? (
} search diff --git a/src/containers/Tenant/Diagnostics/Diagnostics.tsx b/src/containers/Tenant/Diagnostics/Diagnostics.tsx index 18f5dbfb2a..71667817bf 100644 --- a/src/containers/Tenant/Diagnostics/Diagnostics.tsx +++ b/src/containers/Tenant/Diagnostics/Diagnostics.tsx @@ -13,7 +13,7 @@ import { import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants'; import {setDiagnosticsTab} from '../../../store/reducers/tenant/tenant'; import type {AdditionalNodesProps, AdditionalTenantsProps} from '../../../types/additionalProps'; -import type {EPathType} from '../../../types/api/schema'; +import type {EPathSubType, EPathType} from '../../../types/api/schema'; import {cn} from '../../../utils/cn'; import {useTypedDispatch, useTypedSelector} from '../../../utils/hooks'; import {Heatmap} from '../../Heatmap'; @@ -40,6 +40,7 @@ import './Diagnostics.scss'; interface DiagnosticsProps { type?: EPathType; + subType?: EPathSubType; tenantName: string; path: string; additionalTenantProps?: AdditionalTenantsProps; @@ -62,7 +63,7 @@ function Diagnostics(props: DiagnosticsProps) { const hasFeatureFlags = useFeatureFlagsAvailable(); const hasTopicData = useTopicDataAvailable(); - const pages = getPagesByType(props.type, { + const pages = getPagesByType(props.type, props.subType, { hasFeatureFlags, hasTopicData, isTopLevel: props.path === props.tenantName, @@ -129,7 +130,7 @@ function Diagnostics(props: DiagnosticsProps) { ); } case TENANT_DIAGNOSTICS_TABS_IDS.describe: { - return ; + return ; } case TENANT_DIAGNOSTICS_TABS_IDS.hotKeys: { return ; diff --git a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts index e58181d776..64ea644caa 100644 --- a/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts +++ b/src/containers/Tenant/Diagnostics/DiagnosticsPages.ts @@ -4,7 +4,7 @@ import {StringParam, useQueryParams} from 'use-query-params'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../store/reducers/tenant/constants'; import type {TenantDiagnosticsTab} from '../../../store/reducers/tenant/types'; -import {EPathType} from '../../../types/api/schema'; +import {EPathSubType, EPathType} from '../../../types/api/schema'; import type {TenantQuery} from '../TenantPages'; import {TenantTabsGroups, getTenantPath} from '../TenantPages'; import {isDatabaseEntityType, isTopicEntityType} from '../utils/schema'; @@ -113,7 +113,8 @@ const COLUMN_TABLE_PAGES = [overview, schema, topShards, nodes, tablets, describ const DIR_PAGES = [overview, topShards, nodes, describe]; -const CDC_STREAM_PAGES = [overview, consumers, partitions, nodes, tablets, describe]; +const CDC_STREAM_PAGES = [overview, consumers, partitions, nodes, describe]; +const CDC_STREAM_IMPL_PAGES = [overview, nodes, tablets, describe]; const TOPIC_PAGES = [overview, consumers, partitions, topicData, nodes, tablets, describe]; const EXTERNAL_DATA_SOURCE_PAGES = [overview, describe]; @@ -149,15 +150,23 @@ const pathTypeToPages: Record = { [EPathType.EPathTypeTransfer]: TRANSFER_PAGES, [EPathType.EPathTypeResourcePool]: DIR_PAGES, }; +const pathSubTypeToPages: Record = { + [EPathSubType.EPathSubTypeStreamImpl]: CDC_STREAM_IMPL_PAGES, + + [EPathSubType.EPathSubTypeSyncIndexImplTable]: undefined, + [EPathSubType.EPathSubTypeAsyncIndexImplTable]: undefined, + [EPathSubType.EPathSubTypeEmpty]: undefined, +}; export const getPagesByType = ( type?: EPathType, + subType?: EPathSubType, options?: {hasFeatureFlags?: boolean; hasTopicData?: boolean; isTopLevel?: boolean}, ) => { - if (!type || !pathTypeToPages[type]) { - return DIR_PAGES; - } - let pages = pathTypeToPages[type]; + const subTypePages = subType ? pathSubTypeToPages[subType] : undefined; + const typePages = type ? pathTypeToPages[type] : undefined; + let pages = subTypePages || typePages || DIR_PAGES; + if (isTopicEntityType(type) && !options?.hasTopicData) { return pages?.filter((item) => item.id !== TENANT_DIAGNOSTICS_TABS_IDS.topicData); } diff --git a/src/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx b/src/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx index 18104e061e..d4d984ec4a 100644 --- a/src/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +++ b/src/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx @@ -7,13 +7,9 @@ import { import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema'; import {getEntityName} from '../../../utils'; import {TopicStats} from '../TopicStats'; -import {prepareTopicSchemaInfo} from '../utils'; -const prepareChangefeedInfo = ( - changefeedData?: TEvDescribeSchemeResult, - topicData?: TEvDescribeSchemeResult, -): Array => { - if (!changefeedData && !topicData) { +const prepareChangefeedInfo = (changefeedData?: TEvDescribeSchemeResult): Array => { + if (!changefeedData) { return []; } @@ -25,17 +21,14 @@ const prepareChangefeedInfo = ( Mode, Format, }); - const topicInfo = prepareTopicSchemaInfo(topicData); - - const info = [...changefeedInfo, ...topicInfo]; const createStep = changefeedData?.PathDescription?.Self?.CreateStep; if (Number(createStep)) { - info.unshift(formatCommonItem('CreateStep', createStep)); + changefeedInfo.unshift(formatCommonItem('CreateStep', createStep)); } - return info; + return changefeedInfo; }; interface ChangefeedProps { @@ -46,16 +39,16 @@ interface ChangefeedProps { } /** Displays overview for CDCStream EPathType */ -export const ChangefeedInfo = ({path, database, data, topic}: ChangefeedProps) => { +export const ChangefeedInfo = ({path, database, data}: ChangefeedProps) => { const entityName = getEntityName(data?.PathDescription); - if (!data || !topic) { + if (!data) { return
No {entityName} data
; } return (
- +
); diff --git a/src/containers/Tenant/Diagnostics/Overview/Overview.tsx b/src/containers/Tenant/Diagnostics/Overview/Overview.tsx index 6e67378901..57c6169762 100644 --- a/src/containers/Tenant/Diagnostics/Overview/Overview.tsx +++ b/src/containers/Tenant/Diagnostics/Overview/Overview.tsx @@ -1,20 +1,14 @@ import React from 'react'; -import {shallowEqual} from 'react-redux'; - import {ResponseError} from '../../../../components/Errors/ResponseError'; import {TableIndexInfo} from '../../../../components/InfoViewer/schemaInfo'; import {Loader} from '../../../../components/Loader'; -import { - selectSchemaMergedChildrenPaths, - useGetMultiOverviewQuery, -} from '../../../../store/reducers/overview/overview'; +import {overviewApi} from '../../../../store/reducers/overview/overview'; import {EPathType} from '../../../../types/api/schema'; -import {useAutoRefreshInterval, useTypedSelector} from '../../../../utils/hooks'; +import {useAutoRefreshInterval} from '../../../../utils/hooks'; import {ExternalDataSourceInfo} from '../../Info/ExternalDataSource/ExternalDataSource'; import {ExternalTableInfo} from '../../Info/ExternalTable/ExternalTable'; import {ViewInfo} from '../../Info/View/View'; -import {isEntityWithMergedImplementation} from '../../utils/schema'; import {AsyncReplicationInfo} from './AsyncReplicationInfo'; import {ChangefeedInfo} from './ChangefeedInfo'; @@ -31,37 +25,15 @@ interface OverviewProps { function Overview({type, path, database}: OverviewProps) { const [autoRefreshInterval] = useAutoRefreshInterval(); - const isEntityWithMergedImpl = isEntityWithMergedImplementation(type); - - // shallowEqual prevents rerenders when new schema data is loaded - const mergedChildrenPaths = useTypedSelector( - (state) => selectSchemaMergedChildrenPaths(state, path, type, database), - shallowEqual, + const {currentData, isFetching, error} = overviewApi.useGetOverviewQuery( + {path, database}, + {pollingInterval: autoRefreshInterval}, ); - let paths: string[] = []; - if (!isEntityWithMergedImpl) { - paths = [path]; - } else if (mergedChildrenPaths) { - paths = [path, ...mergedChildrenPaths]; - } - - const { - mergedDescribe, - loading: entityLoading, - error, - } = useGetMultiOverviewQuery({ - paths, - database, - autoRefreshInterval, - }); - - const rawData = mergedDescribe[path]; - - const entityNotReady = isEntityWithMergedImpl && !mergedChildrenPaths; + const loading = isFetching && currentData === undefined; const renderContent = () => { - const data = rawData ?? undefined; + const data = currentData ?? undefined; // verbose mapping to guarantee a correct render for new path types // TS will error when a new type is added but not mapped here const pathTypeToComponent: Record React.ReactNode) | undefined> = { @@ -74,20 +46,9 @@ function Overview({type, path, database}: OverviewProps) { [EPathType.EPathTypeExtSubDomain]: undefined, [EPathType.EPathTypeColumnStore]: undefined, [EPathType.EPathTypeColumnTable]: undefined, - [EPathType.EPathTypeCdcStream]: () => { - const topicPath = mergedChildrenPaths?.[0]; - if (topicPath) { - return ( - - ); - } - return undefined; - }, + [EPathType.EPathTypeCdcStream]: () => ( + + ), [EPathType.EPathTypePersQueueGroup]: () => ( ), @@ -103,14 +64,14 @@ function Overview({type, path, database}: OverviewProps) { return (type && pathTypeToComponent[type]?.()) || ; }; - if (entityLoading || entityNotReady) { + if (loading) { return ; } return ( {error ? : null} - {error && !rawData ? null : renderContent()} + {error && !currentData ? null : renderContent()} ); } diff --git a/src/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx b/src/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx index ac4a82ef5c..314ca4f606 100644 --- a/src/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx +++ b/src/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx @@ -1,4 +1,5 @@ import {InfoViewer} from '../../../../../components/InfoViewer'; +import {EPathSubType} from '../../../../../types/api/schema'; import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema'; import {getEntityName} from '../../../utils'; import {TopicStats} from '../TopicStats'; @@ -18,10 +19,19 @@ export const TopicInfo = ({data, path, database}: TopicInfoProps) => { return
No {entityName} data
; } + const renderStats = () => { + // In case of stream impl we display stats in CDC info tab instead + if (data.PathDescription?.Self?.PathSubType === EPathSubType.EPathSubTypeStreamImpl) { + return null; + } + + return ; + }; + return (
- + {renderStats()}
); }; diff --git a/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx b/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx index ed8a6e15b2..6bde55a1db 100644 --- a/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx +++ b/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx @@ -2,7 +2,7 @@ import {useThemeValue} from '@gravity-ui/uikit'; import {TENANT_PAGES_IDS} from '../../../store/reducers/tenant/constants'; import type {AdditionalNodesProps, AdditionalTenantsProps} from '../../../types/additionalProps'; -import type {EPathType} from '../../../types/api/schema'; +import type {EPathSubType, EPathType} from '../../../types/api/schema'; import {cn} from '../../../utils/cn'; import {useTypedSelector} from '../../../utils/hooks'; import Diagnostics from '../Diagnostics/Diagnostics'; @@ -15,6 +15,7 @@ const b = cn('object-general'); interface ObjectGeneralProps { type?: EPathType; + subType?: EPathSubType; tenantName: string; path: string; additionalTenantProps?: AdditionalTenantsProps; @@ -27,7 +28,8 @@ function ObjectGeneral(props: ObjectGeneralProps) { const {tenantPage} = useTypedSelector((state) => state.tenant); const renderPageContent = () => { - const {type, additionalTenantProps, additionalNodesProps, tenantName, path} = props; + const {type, subType, additionalTenantProps, additionalNodesProps, tenantName, path} = + props; switch (tenantPage) { case TENANT_PAGES_IDS.query: { return ; @@ -36,6 +38,7 @@ function ObjectGeneral(props: ObjectGeneralProps) { return ( type === EPathType.EPathT // ==================== -const pathTypeToEntityWithMergedImplementation: Record = { - [EPathType.EPathTypeCdcStream]: true, - - [EPathType.EPathTypePersQueueGroup]: false, - [EPathType.EPathTypeInvalid]: false, - [EPathType.EPathTypeColumnStore]: false, - [EPathType.EPathTypeColumnTable]: false, - [EPathType.EPathTypeDir]: false, - [EPathType.EPathTypeTable]: false, - [EPathType.EPathTypeSubDomain]: false, - [EPathType.EPathTypeTableIndex]: false, - [EPathType.EPathTypeExtSubDomain]: false, - - [EPathType.EPathTypeExternalDataSource]: false, - [EPathType.EPathTypeExternalTable]: false, - - [EPathType.EPathTypeView]: false, - - [EPathType.EPathTypeReplication]: false, - [EPathType.EPathTypeTransfer]: false, - [EPathType.EPathTypeResourcePool]: false, -}; - -export const isEntityWithMergedImplementation = (type?: EPathType) => - (type && pathTypeToEntityWithMergedImplementation[type]) ?? false; - -// ==================== - const pathSubTypeToChildless: Record = { [EPathSubType.EPathSubTypeSyncIndexImplTable]: true, [EPathSubType.EPathSubTypeAsyncIndexImplTable]: true, + [EPathSubType.EPathSubTypeStreamImpl]: true, - [EPathSubType.EPathSubTypeStreamImpl]: false, [EPathSubType.EPathSubTypeEmpty]: false, }; const pathTypeToChildless: Record = { - [EPathType.EPathTypeCdcStream]: true, + [EPathType.EPathTypeCdcStream]: false, [EPathType.EPathTypePersQueueGroup]: true, [EPathType.EPathTypeExternalDataSource]: true, diff --git a/src/store/reducers/overview/overview.ts b/src/store/reducers/overview/overview.ts index 9c1676c432..2beb11b2f5 100644 --- a/src/store/reducers/overview/overview.ts +++ b/src/store/reducers/overview/overview.ts @@ -1,36 +1,7 @@ -import {createSelector} from '@reduxjs/toolkit'; -import {skipToken} from '@reduxjs/toolkit/query'; - -import {isEntityWithMergedImplementation} from '../../../containers/Tenant/utils/schema'; -import type {EPathType} from '../../../types/api/schema'; -import type {IDescribeData} from '../../../types/store/describe'; -import type {RootState} from '../../defaultStore'; import {api} from '../api'; export const overviewApi = api.injectEndpoints({ endpoints: (build) => ({ - getMultiOverview: build.query({ - queryFn: async ({paths, database}: {paths: string[]; database: string}, {signal}) => { - try { - const data = await Promise.all( - paths.map((p) => - window.api.viewer.getDescribe( - { - path: p, - database, - }, - {signal}, - ), - ), - ); - return {data}; - } catch (error) { - return {error}; - } - }, - keepUnusedDataFor: 0, - providesTags: ['All'], - }), getOverview: build.query({ queryFn: async ( {path, database, timeout}: {path: string; database: string; timeout?: number}, @@ -59,86 +30,3 @@ export const overviewApi = api.injectEndpoints({ }), }), }); - -const getOverviewSelector = createSelector( - (path: string) => path, - (_path: string, database: string) => database, - (path, database) => overviewApi.endpoints.getOverview.select({path, database}), -); - -const selectGetOverview = createSelector( - (state: RootState) => state, - (_state: RootState, path: string, database: string) => getOverviewSelector(path, database), - (state, selectOverview) => selectOverview(state).data, -); - -const selectOverviewChildren = (state: RootState, path: string, database: string) => - selectGetOverview(state, path, database)?.PathDescription?.Children; - -export const selectSchemaMergedChildrenPaths = createSelector( - [ - (_, path: string) => path, - (_, _path, type: EPathType | undefined) => type, - (state, path, _type, database: string) => selectOverviewChildren(state, path, database), - ], - (path, type, children) => { - return isEntityWithMergedImplementation(type) - ? children?.map(({Name}) => path + '/' + Name) - : undefined; - }, -); - -//this hook is done not to refetch mainPath describe for entities with merged implementation -export function useGetMultiOverviewQuery({ - paths, - database, - autoRefreshInterval, -}: { - paths: string[]; - database: string; - autoRefreshInterval?: number; -}) { - const [mainPath, ...additionalPaths] = paths; - - const { - currentData: mainDescribe, - isFetching: mainDescribeIsFetching, - error: mainDescribeError, - } = overviewApi.useGetOverviewQuery( - { - path: mainPath, - database, - }, - { - pollingInterval: autoRefreshInterval, - }, - ); - - const { - currentData: currentChindrenDescribe, - isFetching: childrenDescribeIsFetching, - error: childrenDescribeError, - } = overviewApi.useGetMultiOverviewQuery( - additionalPaths.length ? {paths: additionalPaths, database} : skipToken, - { - pollingInterval: autoRefreshInterval, - }, - ); - - const childrenDescribeLoading = - childrenDescribeIsFetching && currentChindrenDescribe === undefined; - const mainDescribeLoading = mainDescribeIsFetching && mainDescribe === undefined; - - const loading = mainDescribeLoading || childrenDescribeLoading; - - const describe = [mainDescribe, ...(currentChindrenDescribe ?? [])]; - - const mergedDescribe = describe.reduce((acc, item) => { - if (item?.Path) { - acc[item.Path] = item; - } - return acc; - }, {}); - - return {loading, error: mainDescribeError || childrenDescribeError, mergedDescribe}; -}