@@ -6,7 +6,11 @@ import {
6
6
UseFormSetValue ,
7
7
UseFormWatch ,
8
8
} from 'react-hook-form' ;
9
- import { SourceKind , TSource } from '@hyperdx/common-utils/dist/types' ;
9
+ import {
10
+ MetricsDataType ,
11
+ SourceKind ,
12
+ TSource ,
13
+ } from '@hyperdx/common-utils/dist/types' ;
10
14
import {
11
15
Anchor ,
12
16
Box ,
@@ -32,6 +36,7 @@ import { IS_METRICS_ENABLED, IS_SESSIONS_ENABLED } from '@/config';
32
36
import { useConnections } from '@/connection' ;
33
37
import {
34
38
inferTableSourceConfig ,
39
+ isValidMetricTable ,
35
40
useCreateSource ,
36
41
useDeleteSource ,
37
42
useSource ,
@@ -641,10 +646,43 @@ export function MetricTableModelForm({
641
646
setValue : UseFormSetValue < TSource > ;
642
647
} ) {
643
648
const databaseName = watch ( `from.databaseName` , DEFAULT_DATABASE ) ;
644
- const tableName = watch ( `from.tableName` ) ;
645
649
const connectionId = watch ( `connection` ) ;
646
650
647
- const [ showOptionalFields , setShowOptionalFields ] = useState ( false ) ;
651
+ useEffect ( ( ) => {
652
+ setValue ( 'timestampValueExpression' , 'TimeUnix' ) ;
653
+ const { unsubscribe } = watch ( async ( value , { name, type } ) => {
654
+ try {
655
+ if ( name && type === 'change' ) {
656
+ const [ prefix , suffix ] = name . split ( '.' ) ;
657
+ if ( prefix === 'metricTables' ) {
658
+ const tableName =
659
+ value ?. metricTables ?. [ suffix as keyof typeof value . metricTables ] ;
660
+ const metricType = suffix as MetricsDataType ;
661
+ const isValid = await isValidMetricTable ( {
662
+ databaseName,
663
+ tableName,
664
+ connectionId,
665
+ metricType,
666
+ } ) ;
667
+ if ( ! isValid ) {
668
+ notifications . show ( {
669
+ color : 'red' ,
670
+ message : `${ tableName } is not a valid OTEL ${ metricType } schema.` ,
671
+ } ) ;
672
+ }
673
+ }
674
+ }
675
+ } catch ( e ) {
676
+ console . error ( e ) ;
677
+ notifications . show ( {
678
+ color : 'red' ,
679
+ message : e . message ,
680
+ } ) ;
681
+ }
682
+ } ) ;
683
+
684
+ return ( ) => unsubscribe ( ) ;
685
+ } , [ setValue , watch , databaseName , connectionId ] ) ;
648
686
649
687
return (
650
688
< >
@@ -659,169 +697,20 @@ export function MetricTableModelForm({
659
697
name = { `from.databaseName` }
660
698
/>
661
699
</ FormRow >
662
- < Divider />
663
- < FormRow label = { 'Metric Type' } >
664
- < Select
665
- data = { [ { value : 'gauge' , label : 'Gauge' } ] }
666
- defaultValue = "gauge"
667
- placeholder = "Select metric type"
668
- allowDeselect = { false }
669
- />
670
- </ FormRow >
671
- < FormRow label = { 'Table' } >
672
- < DBTableSelectControlled
673
- connectionId = { connectionId }
674
- database = { databaseName }
675
- control = { control }
676
- name = { `from.tableName` }
677
- rules = { { required : 'Table is required' } }
678
- />
679
- </ FormRow >
680
- < FormRow label = { 'Timestamp Column' } >
681
- < SQLInlineEditorControlled
682
- connectionId = { connectionId }
683
- database = { databaseName }
684
- table = { tableName }
685
- control = { control }
686
- name = "timestampValueExpression"
687
- placeholder = "TimeUnix"
688
- disableKeywordAutocomplete
689
- />
690
- </ FormRow >
691
- < FormRow
692
- label = { 'Default Select' }
693
- helpText = "Default columns selected in search results (this can be customized per search later)"
694
- >
695
- < SQLInlineEditorControlled
696
- database = { databaseName }
697
- table = { tableName }
698
- control = { control }
699
- name = "defaultTableSelectExpression"
700
- placeholder = "TimeUnix, MetricName, Value, ServiceName, Attributes"
701
- connectionId = { connectionId }
702
- />
703
- </ FormRow >
704
- < FormRow
705
- label = { 'Metric Name Column' }
706
- helpText = "Column containing the name of the metric being measured"
707
- >
708
- < SQLInlineEditorControlled
709
- connectionId = { connectionId }
710
- database = { databaseName }
711
- table = { tableName }
712
- control = { control }
713
- name = "metricNameExpression"
714
- placeholder = "MetricName"
715
- />
716
- </ FormRow >
717
- < FormRow label = { 'Gauge Value Column' } >
718
- < SQLInlineEditorControlled
719
- connectionId = { connectionId }
720
- database = { databaseName }
721
- table = { tableName }
722
- control = { control }
723
- name = "valueExpression"
724
- placeholder = "Value"
725
- />
726
- </ FormRow >
727
- < Box >
728
- { ! showOptionalFields && (
729
- < Anchor
730
- underline = "always"
731
- onClick = { ( ) => setShowOptionalFields ( true ) }
732
- size = "xs"
733
- c = "gray.4"
734
- >
735
- < Text me = "sm" span >
736
- < i className = "bi bi-gear" />
737
- </ Text >
738
- Configure Optional Fields
739
- </ Anchor >
740
- ) }
741
- { showOptionalFields && (
742
- < Button
743
- onClick = { ( ) => setShowOptionalFields ( false ) }
744
- size = "xs"
745
- variant = "subtle"
746
- color = "gray.4"
747
- >
748
- Hide Optional Fields
749
- </ Button >
750
- ) }
751
- </ Box >
752
- </ Stack >
753
- < Stack
754
- gap = "sm"
755
- style = { {
756
- display : showOptionalFields ? 'flex' : 'none' ,
757
- } }
758
- >
759
- < Divider />
760
- < FormRow
761
- label = { 'Service Name Column' }
762
- helpText = "Column containing the service name associated with the metric"
763
- >
764
- < SQLInlineEditorControlled
765
- connectionId = { connectionId }
766
- database = { databaseName }
767
- table = { tableName }
768
- control = { control }
769
- name = "serviceNameExpression"
770
- placeholder = "ServiceName"
771
- />
772
- </ FormRow >
773
- < FormRow
774
- label = { 'Resource Attributes Column' }
775
- helpText = "Column containing resource attributes/tags associated with the metric"
776
- >
777
- < SQLInlineEditorControlled
778
- connectionId = { connectionId }
779
- database = { databaseName }
780
- table = { tableName }
781
- control = { control }
782
- name = "resourceAttributesExpression"
783
- placeholder = "ResourceAttributes"
784
- />
785
- </ FormRow >
786
- < FormRow
787
- label = { 'Metric Unit Column' }
788
- helpText = "Column containing the unit of measurement for the metric"
789
- >
790
- < SQLInlineEditorControlled
791
- connectionId = { connectionId }
792
- database = { databaseName }
793
- table = { tableName }
794
- control = { control }
795
- name = "metricUnitExpression"
796
- placeholder = "MetricUnit"
797
- />
798
- </ FormRow >
799
- < FormRow
800
- label = { 'Metric Flag Column' }
801
- helpText = "Column containing flags or markers associated with the metric"
802
- >
803
- < SQLInlineEditorControlled
804
- connectionId = { connectionId }
805
- database = { databaseName }
806
- table = { tableName }
807
- control = { control }
808
- name = "flagsExpression"
809
- placeholder = "Flags"
810
- />
811
- </ FormRow >
812
- < FormRow
813
- label = { 'Event Attributes Expression' }
814
- helpText = "Column containing additional attributes/dimensions for the metric"
815
- >
816
- < SQLInlineEditorControlled
817
- connectionId = { connectionId }
818
- database = { databaseName }
819
- table = { tableName }
820
- control = { control }
821
- name = "eventAttributesExpression"
822
- placeholder = "Attributes"
823
- />
824
- </ FormRow >
700
+ { Object . keys ( MetricsDataType ) . map ( metricType => (
701
+ < FormRow
702
+ key = { metricType . toLowerCase ( ) }
703
+ label = { `${ metricType } Table` }
704
+ helpText = { `Table containing ${ metricType . toLowerCase ( ) } metrics data` }
705
+ >
706
+ < DBTableSelectControlled
707
+ connectionId = { connectionId }
708
+ database = { databaseName }
709
+ control = { control }
710
+ name = { `metricTables.${ metricType . toLowerCase ( ) } ` }
711
+ />
712
+ </ FormRow >
713
+ ) ) }
825
714
</ Stack >
826
715
</ >
827
716
) ;
@@ -1076,7 +965,7 @@ export function TableSourceForm({
1076
965
< Radio value = { SourceKind . Log } label = "Log" />
1077
966
< Radio value = { SourceKind . Trace } label = "Trace" />
1078
967
{ IS_METRICS_ENABLED && (
1079
- < Radio value = { SourceKind . Metric } label = "Metric " />
968
+ < Radio value = { SourceKind . Metric } label = "OTEL Metrics " />
1080
969
) }
1081
970
{ IS_SESSIONS_ENABLED && (
1082
971
< Radio value = { SourceKind . Session } label = "Session" />
0 commit comments