import React, { Fragment, useCallback, useEffect, useRef, useState, } from 'react'; import { Control, Controller, useFieldArray, useForm, UseFormSetValue, useWatch, } from 'react-hook-form'; import { z } from 'zod'; import { ClickHouseQueryError } from '@hyperdx/common-utils/dist/clickhouse'; import { MetricsDataType, SourceKind, sourceSchemaWithout, TSource, TSourceUnion, } from '@hyperdx/common-utils/dist/types'; import { ActionIcon, Anchor, Badge, Box, Button, Center, Divider, Flex, Grid, Group, Radio, Select, Slider, Stack, Text, Tooltip, } from '@mantine/core'; import { DateInput } from '@mantine/dates'; import { notifications } from '@mantine/notifications'; import { IconCheck, IconCirclePlus, IconHelpCircle, IconSettings, IconTrash, } from '@tabler/icons-react'; import { SourceSelectControlled } from '@/components/SourceSelect'; import { IS_METRICS_ENABLED, IS_SESSIONS_ENABLED } from '@/config'; import { useConnections } from '@/connection'; import { useExplainQuery } from '@/hooks/useExplainQuery'; import { useMetadataWithSettings } from '@/hooks/useMetadata'; import { inferTableSourceConfig, isValidMetricTable, isValidSessionsTable, useCreateSource, useDeleteSource, useSource, useSources, useUpdateSource, } from '@/source'; import { inferMaterializedViewConfig, MV_AGGREGATE_FUNCTIONS, MV_GRANULARITY_OPTIONS, } from '@/utils/materializedViews'; import ConfirmDeleteMenu from '../ConfirmDeleteMenu'; import { ConnectionSelectControlled } from '../ConnectionSelect'; import { DatabaseSelectControlled } from '../DatabaseSelect'; import { DBTableSelectControlled } from '../DBTableSelect'; import { ErrorCollapse } from '../Error/ErrorCollapse'; import { InputControlled } from '../InputControlled'; import SelectControlled from '../SelectControlled'; import { SQLInlineEditorControlled } from '../SQLInlineEditor'; const DEFAULT_DATABASE = 'default'; const MV_AGGREGATE_FUNCTION_OPTIONS = MV_AGGREGATE_FUNCTIONS.map(fn => ({ value: fn, label: fn, })); // TODO: maybe otel clickhouse export migrate the schema? const OTEL_CLICKHOUSE_EXPRESSIONS = { timestampValueExpression: 'TimeUnix', resourceAttributesExpression: 'ResourceAttributes', }; const CORRELATION_FIELD_MAP: Record< SourceKind, Record > = { [SourceKind.Log]: { metricSourceId: [ { targetKind: SourceKind.Metric, targetField: 'logSourceId' }, ], traceSourceId: [ { targetKind: SourceKind.Trace, targetField: 'logSourceId' }, ], }, [SourceKind.Trace]: { logSourceId: [{ targetKind: SourceKind.Log, targetField: 'traceSourceId' }], sessionSourceId: [ { targetKind: SourceKind.Session, targetField: 'traceSourceId' }, ], metricSourceId: [ { targetKind: SourceKind.Metric, targetField: 'logSourceId' }, ], }, [SourceKind.Session]: { traceSourceId: [ { targetKind: SourceKind.Trace, targetField: 'sessionSourceId' }, ], }, [SourceKind.Metric]: { logSourceId: [ { targetKind: SourceKind.Log, targetField: 'metricSourceId' }, ], }, }; function FormRow({ label, children, helpText, }: { label: React.ReactNode; children: React.ReactNode; helpText?: string; }) { return ( // {typeof label === 'string' ? ( {label} ) : ( label )}
{children}
); } type HighlightedAttributeRowProps = Omit & { id: string; index: number; databaseName: string; name: | 'highlightedTraceAttributeExpressions' | 'highlightedRowAttributeExpressions'; tableName: string; connectionId: string; removeHighlightedAttribute: (index: number) => void; }; function HighlightedAttributeRow({ id, index, control, databaseName, name, tableName, connectionId, removeHighlightedAttribute, }: HighlightedAttributeRowProps) { const expression = useWatch({ control, name: `${name}.${index}.sqlExpression`, }); const alias = useWatch({ control, name: `${name}.${index}.alias`, }); const { data: explainData, error: explainError, isLoading: explainLoading, refetch: explainExpression, } = useExplainQuery( { from: { databaseName, tableName }, connection: connectionId, select: [{ alias, valueExpression: expression }], where: '', }, { enabled: false }, ); const runExpression = () => { if (expression) { explainExpression(); } }; const isExpressionValid = !!explainData?.length; const isExpressionInvalid = explainError instanceof ClickHouseQueryError; return (
AS removeHighlightedAttribute(index)} > {(isExpressionValid || isExpressionInvalid) && ( {isExpressionValid && ( Expression is valid. )} {isExpressionInvalid && ( )} )}
); } function HighlightedAttributeExpressionsFormRow({ control, name, label, helpText, }: Omit & { name: | 'highlightedTraceAttributeExpressions' | 'highlightedRowAttributeExpressions'; label: string; helpText?: string; }) { const databaseName = useWatch({ control, name: 'from.databaseName', defaultValue: DEFAULT_DATABASE, }); const tableName = useWatch({ control, name: 'from.tableName' }); const connectionId = useWatch({ control, name: 'connection' }); const { fields: highlightedAttributes, append: appendHighlightedAttribute, remove: removeHighlightedAttribute, } = useFieldArray({ control, name, }); return ( {highlightedAttributes.map(({ id }, index) => ( ))} ); } /** Component for configuring one or more materialized views */ function MaterializedViewsFormSection({ control, setValue }: TableModelProps) { const databaseName = useWatch({ control, name: `from.databaseName`, defaultValue: DEFAULT_DATABASE, }); const { fields: materializedViews, append: appendMaterializedView, remove: removeMaterializedView, } = useFieldArray({ control, name: 'materializedViews', }); return ( {materializedViews.map((field, index) => ( removeMaterializedView(index)} /> ))} ); } /** Component for configuring a single materialized view */ function MaterializedViewFormSection({ control, mvIndex, onRemove, setValue, }: { mvIndex: number; onRemove: () => void } & TableModelProps) { const connection = useWatch({ control, name: `connection` }); const sourceDatabaseName = useWatch({ control, name: `from.databaseName`, defaultValue: DEFAULT_DATABASE, }); const mvDatabaseName = useWatch({ control, name: `materializedViews.${mvIndex}.databaseName`, defaultValue: sourceDatabaseName, }); const mvTableName = useWatch({ control, name: `materializedViews.${mvIndex}.tableName`, defaultValue: '', }); return ( Timestamp Column Granularity (