import React from 'react'; import {dateTimeParse} from '@gravity-ui/date-utils'; import {Label} from '@gravity-ui/uikit'; import {Loader} from '../../../../../components/Loader'; import type {YDBDefinitionListItem} from '../../../../../components/YDBDefinitionList/YDBDefinitionList'; import {YDBDefinitionList} from '../../../../../components/YDBDefinitionList/YDBDefinitionList'; import {YQLCodePreview} from '../../../../../components/YQLCodePreview/YQLCodePreview'; import {streamingQueriesApi} from '../../../../../store/reducers/streamingQuery/streamingQuery'; import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema'; import {EPathType} from '../../../../../types/api/schema'; import type {IQueryResult} from '../../../../../types/store/query'; import {cn} from '../../../../../utils/cn'; import {EMPTY_DATA_PLACEHOLDER} from '../../../../../utils/constants'; import { getStringifiedData, stripIndentByFirstLine, trimOuterEmptyLines, } from '../../../../../utils/dataFormatters/dataFormatters'; import {parseIssuesData} from '../../../../../utils/query'; import {ResultIssuesModal} from '../../../Query/Issues/Issues'; import {SchemaObjectInfo} from '../SchemaObjectInfo/SchemaObjectInfo'; import i18n from './i18n'; import './StreamingQueryInfo.scss'; interface StreamingQueryProps { database: string; path: string; data?: TEvDescribeSchemeResult; } const b = cn('ydb-streaming-query-info'); export function StreamingQueryInfo({database, path, data}: StreamingQueryProps) { const {currentData: sysData, isFetching} = streamingQueriesApi.useGetStreamingQueryInfoQuery( {database, path}, {skip: !database || !path}, ); const loading = isFetching && sysData === undefined; const {items, queryText, createdContent, stateItems} = prepareStreamingQueryInfo( sysData, data?.PathDescription?.Self?.CreateStep, ); return ( {loading ? ( ) : items.length ? ( ) : null} {queryText ? ( ) : null} ); } const STATE_THEME_MAP: Record['theme']> = { CREATING: 'info', CREATED: 'normal', STARTING: 'info', RUNNING: 'success', STOPPING: 'info', STOPPED: 'normal', COMPLETED: 'success', SUSPENDED: 'warning', FAILED: 'danger', }; function StateLabel({state}: {state?: string}) { if (!state) { return null; } const theme = STATE_THEME_MAP[state] ?? 'normal'; return ; } export function prepareStreamingQueryInfo(sysData?: IQueryResult, createStep?: string | number) { if (!sysData) { return {items: [], queryText: undefined, createdContent: undefined, stateItems: []}; } const row = sysData.resultSets?.[0]?.result?.[0]; const createdContent = formatLifecycleValue(row?.CreatedAt, row?.CreatedBy, createStep) ?? (row && 'CreatedAt' in row ? EMPTY_DATA_PLACEHOLDER : undefined); const lifecycleFields = [ {name: i18n('field_started'), timestamp: 'StartedAt', user: 'StartedBy'}, {name: i18n('field_modified'), timestamp: 'ModifiedAt', user: 'ModifiedBy'}, {name: i18n('field_stopped'), timestamp: 'FinishedAt', user: 'StoppedBy'}, ]; const info: YDBDefinitionListItem[] = lifecycleFields .filter(({timestamp}) => row && timestamp in row) .map(({name, timestamp, user}) => ({ name, content: formatLifecycleValue(row?.[timestamp], row?.[user]) ?? EMPTY_DATA_PLACEHOLDER, })); const state = getStringifiedData(row?.Status); const queryText = getStringifiedData(row?.Text); let normalizedQueryText = trimOuterEmptyLines(queryText); normalizedQueryText = stripIndentByFirstLine(normalizedQueryText); const errorRaw = row?.Issues; // We use custom error check, because error type can be non-standard const errorData = parseIssuesData(errorRaw); const stateItems: YDBDefinitionListItem[] = [ {name: i18n('field_query-state'), content: }, ]; if (errorData) { info.push({ name: i18n('field_query-error'), content: , }); } return {items: info, queryText: normalizedQueryText, createdContent, stateItems}; } function formatLifecycleValue( timestamp: string | number | null | undefined, user: string | number | null | undefined, fallback?: string | number, ) { let milliseconds = NaN; if (typeof timestamp === 'number') { milliseconds = timestamp; } else if (typeof timestamp === 'string') { milliseconds = Date.parse(timestamp); } const dateMilliseconds = Number.isFinite(milliseconds) && milliseconds > 0 ? milliseconds : Number(fallback); const date = Number.isFinite(dateMilliseconds) && dateMilliseconds > 0 ? dateTimeParse(dateMilliseconds)?.format('YYYY-MM-DD HH:mm:ss') : undefined; if (!date && !user) { return undefined; } if (!user) { return date; } return ( {i18n('value_date-by-user', {date: date || EMPTY_DATA_PLACEHOLDER})}{' '} ); }