From fe285e52e89f41658b87882d9110747b26ca3158 Mon Sep 17 00:00:00 2001 From: Karl Power Date: Thu, 8 Jan 2026 18:37:22 +0100 Subject: [PATCH] feat: allow validating highlighted attribute expressions when creating --- .../app/src/components/Sources/SourceForm.tsx | 233 +++++++++++++----- packages/app/src/hooks/useExplainQuery.tsx | 9 +- 2 files changed, 175 insertions(+), 67 deletions(-) diff --git a/packages/app/src/components/Sources/SourceForm.tsx b/packages/app/src/components/Sources/SourceForm.tsx index 78fc712c7..e0221e246 100644 --- a/packages/app/src/components/Sources/SourceForm.tsx +++ b/packages/app/src/components/Sources/SourceForm.tsx @@ -8,6 +8,7 @@ import { useWatch, } from 'react-hook-form'; import { z } from 'zod'; +import { ClickHouseQueryError } from '@hyperdx/common-utils/dist/clickhouse'; import { MetricsDataType, SourceKind, @@ -41,10 +42,12 @@ import { IconSettings, IconTrash, } from '@tabler/icons-react'; +import { useQuery } from '@tanstack/react-query'; import { SourceSelectControlled } from '@/components/SourceSelect'; import { IS_METRICS_ENABLED, IS_SESSIONS_ENABLED } from '@/config'; import { useConnections } from '@/connection'; +import { useExplainQuery } from '@/hooks/useExplainQuery'; import { inferTableSourceConfig, isValidMetricTable, @@ -168,6 +171,152 @@ function FormRow({ ); } +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) && ( + + Expression is {isExpressionValid ? 'valid' : 'invalid'}. + + )} + + + {isExpressionInvalid && {explainError?.message}} + + + + + + + + + + + + +
+ ); +} + function HighlightedAttributeExpressionsFormRow({ control, name, @@ -200,63 +349,20 @@ function HighlightedAttributeExpressionsFormRow({ return ( - {highlightedAttributes.map((field, index) => ( - - - - - - - AS - - removeHighlightedAttribute(index)} - > - - - - - - - - - - - - - - - + {highlightedAttributes.map(({ id }, index) => ( + ))}