import React from 'react'; import {Flex, Text} from '@gravity-ui/uikit'; import {skipToken} from '@reduxjs/toolkit/query'; import {isNil} from 'lodash'; import {useSchemaTopicDataAvailable} from '../../../../../store/reducers/capabilities/hooks'; import { useClusterProxySettingResolved, useClusterWithProxy, } from '../../../../../store/reducers/cluster/cluster'; import {partitionsApi} from '../../../../../store/reducers/partitions/partitions'; import {topicApi} from '../../../../../store/reducers/topic/topic'; import type {TopicDataRequest} from '../../../../../types/api/topic'; import {useClusterNameFromQuery} from '../../../../../utils/hooks/useDatabaseFromQuery'; import {safeParseNumber} from '../../../../../utils/utils'; import i18n from '../i18n'; import {b} from '../shared'; import type {PreviewContainerProps} from '../types'; import {Preview} from './PreviewView'; import {TopicPreviewTable} from './TopicPreviewTable'; const TOPIC_PREVIEW_LIMIT = 100; export function TopicPreview({database, path, databaseFullPath}: PreviewContainerProps) { const useMetaProxy = useClusterWithProxy(); const schemaTopicDataAvailable = useSchemaTopicDataAvailable(); // Use the meta handler only when meta is reachable (proxied by the cluster). // When meta is not proxied (e.g. behind OIDC), fall back to the viewer handler. const useMeta = schemaTopicDataAvailable && useMetaProxy; // `useClusterWithProxy()` optimistically returns `true` while the cluster // base info is loading. Defer the request until the `use_meta_proxy` setting // is known so a non-proxied (OIDC) cluster never hits the meta handler first. const proxySettingResolved = useClusterProxySettingResolved(); const clusterName = useClusterNameFromQuery(); const { data: partitions, isLoading: partitionsLoading, error: partitionsError, } = partitionsApi.useGetPartitionsQuery({path, database, databaseFullPath, useMetaProxy}); const firstPartition = React.useMemo(() => { return partitions?.[0]; }, [partitions]); const queryParams = React.useMemo(() => { const firstPartitionId = firstPartition?.partitionId; if (!firstPartition || isNil(firstPartitionId) || !proxySettingResolved) { return skipToken; } const params: TopicDataRequest & {clusterName?: string; useMeta?: boolean} = { database, path, clusterName, partition: String(firstPartitionId), limit: TOPIC_PREVIEW_LIMIT, offset: safeParseNumber(firstPartition.endOffset) - TOPIC_PREVIEW_LIMIT, message_size_limit: 100, useMeta, }; return params; }, [database, path, clusterName, firstPartition, useMeta, proxySettingResolved]); const {currentData, error, isFetching} = topicApi.useGetTopicDataQuery(queryParams); const loading = partitionsLoading || (isFetching && currentData === undefined); const offsetsRange = React.useMemo(() => { if (!firstPartition) { return undefined; } return { start: safeParseNumber(firstPartition.startOffset), end: Math.max(safeParseNumber(firstPartition.endOffset) - 1, 0), }; }, [firstPartition]); // A topic has an associated schema when the response carries schema context // (`SchemaPath`). In that case messages are returned already schematized as // JSON values (objects/arrays/primitives, including strings) and must not be // base64-decoded. A failed schematization (`SchematizeError`) falls back to // the legacy base64 shape. const isResponseSchematized = Boolean(currentData?.SchemaPath) && !currentData?.SchematizeError; const renderResult = React.useCallback(() => { return ( {!isNil(firstPartition?.partitionId) && ( {i18n('label_partition-id', {id: firstPartition.partitionId})} )} {offsetsRange && ( ({i18n('label_offsets-range', offsetsRange)}) )} {currentData && ( )} ); }, [offsetsRange, firstPartition, currentData, isResponseSchematized]); const offsetsQuantity = safeParseNumber(currentData?.EndOffset) - safeParseNumber(currentData?.StartOffset); return ( TOPIC_PREVIEW_LIMIT} quantity={currentData?.Messages?.length ?? 0} /> ); }