mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
refactor: table connections standardized and single by default. Special case for dashboard (#1195)
Closes HDX-2469
This commit is contained in:
parent
1cda1485ed
commit
5210bb86a4
25 changed files with 216 additions and 156 deletions
6
.changeset/two-melons-admire.md
Normal file
6
.changeset/two-melons-admire.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
"@hyperdx/common-utils": patch
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
refactor: clean up table connections
|
||||
|
|
@ -1265,7 +1265,7 @@ function DBSearchPage() {
|
|||
</Group>
|
||||
<Box style={{ minWidth: 100, flexGrow: 1 }}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(inputSourceObj)}
|
||||
tableConnection={tcFromSource(inputSourceObj)}
|
||||
control={control}
|
||||
name="select"
|
||||
defaultValue={inputSourceObj?.defaultTableSelectExpression}
|
||||
|
|
@ -1279,7 +1279,7 @@ function DBSearchPage() {
|
|||
</Box>
|
||||
<Box style={{ maxWidth: 400, width: '20%' }}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(inputSourceObj)}
|
||||
tableConnection={tcFromSource(inputSourceObj)}
|
||||
control={control}
|
||||
name="orderBy"
|
||||
defaultValue={defaultOrderBy}
|
||||
|
|
@ -1401,7 +1401,7 @@ function DBSearchPage() {
|
|||
sqlInput={
|
||||
<Box style={{ width: '75%', flexGrow: 1 }}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(inputSourceObj)}
|
||||
tableConnection={tcFromSource(inputSourceObj)}
|
||||
control={control}
|
||||
name="where"
|
||||
placeholder="SQL WHERE clause (ex. column = 'foo')"
|
||||
|
|
@ -1421,7 +1421,7 @@ function DBSearchPage() {
|
|||
}
|
||||
luceneInput={
|
||||
<SearchInputV2
|
||||
tableConnections={tcFromSource(inputSourceObj)}
|
||||
tableConnection={tcFromSource(inputSourceObj)}
|
||||
control={control}
|
||||
name="where"
|
||||
onLanguageChange={lang =>
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ const AlertForm = ({
|
|||
grouped by
|
||||
</Text>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(source)}
|
||||
tableConnection={tcFromSource(source)}
|
||||
control={control}
|
||||
name={`groupBy`}
|
||||
placeholder="SQL Columns"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const DashboardFilterSelect = ({
|
|||
|
||||
const { data: keys, isLoading: isKeyValuesLoading } = useGetKeyValues(
|
||||
{
|
||||
chartConfigs: {
|
||||
chartConfig: {
|
||||
dateRange,
|
||||
timestampValueExpression: timestampValueExpression!,
|
||||
connection: connection!,
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ const DashboardFilterEditForm = ({
|
|||
error={formState.errors.expression}
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tableConnection}
|
||||
tableConnection={tableConnection}
|
||||
control={control}
|
||||
name="expression"
|
||||
placeholder="SQL column or expression"
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ export default function NamespaceDetailsSidePanel({
|
|||
|
||||
const { data: logServiceNames } = useGetKeyValues(
|
||||
{
|
||||
chartConfigs: {
|
||||
chartConfig: {
|
||||
from: logSource.from,
|
||||
where: `${logSource?.resourceAttributesExpression}.k8s.namespace.name:"${namespaceName}"`,
|
||||
whereLanguage: 'lucene',
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ export default function NodeDetailsSidePanel({
|
|||
|
||||
const { data: logServiceNames } = useGetKeyValues(
|
||||
{
|
||||
chartConfigs: {
|
||||
chartConfig: {
|
||||
from: logSource.from,
|
||||
where: `${logSource?.resourceAttributesExpression}.k8s.node.name:"${nodeName}"`,
|
||||
whereLanguage: 'lucene',
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ export default function PodDetailsSidePanel({
|
|||
|
||||
const { data: logServiceNames } = useGetKeyValues(
|
||||
{
|
||||
chartConfigs: {
|
||||
chartConfig: {
|
||||
from: logSource.from,
|
||||
where: `${logSource?.resourceAttributesExpression}.k8s.pod.name:"${podName}"`,
|
||||
whereLanguage: 'lucene',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useController, UseControllerProps } from 'react-hook-form';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { Field, TableConnection } from '@hyperdx/common-utils/dist/metadata';
|
||||
import {
|
||||
Field,
|
||||
TableConnectionChoice,
|
||||
} from '@hyperdx/common-utils/dist/metadata';
|
||||
import { genEnglishExplanation } from '@hyperdx/common-utils/dist/queryParser';
|
||||
|
||||
import AutocompleteInput from '@/AutocompleteInput';
|
||||
|
|
@ -25,6 +28,7 @@ export class LuceneLanguageFormatter implements ILanguageFormatter {
|
|||
|
||||
const luceneLanguageFormatter = new LuceneLanguageFormatter();
|
||||
export default function SearchInputV2({
|
||||
tableConnection,
|
||||
tableConnections,
|
||||
placeholder = 'Search your events for anything...',
|
||||
size = 'sm',
|
||||
|
|
@ -38,7 +42,6 @@ export default function SearchInputV2({
|
|||
'data-testid': dataTestId,
|
||||
...props
|
||||
}: {
|
||||
tableConnections?: TableConnection | TableConnection[];
|
||||
placeholder?: string;
|
||||
size?: 'xs' | 'sm' | 'lg';
|
||||
zIndex?: number;
|
||||
|
|
@ -49,7 +52,8 @@ export default function SearchInputV2({
|
|||
additionalSuggestions?: string[];
|
||||
queryHistoryType?: string;
|
||||
'data-testid'?: string;
|
||||
} & UseControllerProps<any>) {
|
||||
} & UseControllerProps<any> &
|
||||
TableConnectionChoice) {
|
||||
const {
|
||||
field: { onChange, value },
|
||||
} = useController(props);
|
||||
|
|
@ -59,9 +63,9 @@ export default function SearchInputV2({
|
|||
|
||||
const autoCompleteOptions = useAutoCompleteOptions(
|
||||
luceneLanguageFormatter,
|
||||
value != null ? `${value}` : '', // value can be null at times
|
||||
value != null ? `${value}` : '',
|
||||
{
|
||||
tableConnections,
|
||||
tableConnection: tableConnection ? tableConnection : tableConnections,
|
||||
additionalSuggestions,
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -955,7 +955,7 @@ function ServicesDashboardPage() {
|
|||
control={control}
|
||||
sqlInput={
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(source)}
|
||||
tableConnection={tcFromSource(source)}
|
||||
onSubmit={onSubmit}
|
||||
control={control}
|
||||
name="where"
|
||||
|
|
@ -973,7 +973,7 @@ function ServicesDashboardPage() {
|
|||
}
|
||||
luceneInput={
|
||||
<SearchInputV2
|
||||
tableConnections={tcFromSource(source)}
|
||||
tableConnection={tcFromSource(source)}
|
||||
control={control}
|
||||
name="where"
|
||||
onLanguageChange={lang =>
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ export default function SessionSubpanel({
|
|||
>
|
||||
{whereLanguage === 'sql' ? (
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(traceSource)}
|
||||
tableConnection={tcFromSource(traceSource)}
|
||||
control={control}
|
||||
name="where"
|
||||
placeholder="SQL WHERE clause (ex. column = 'foo')"
|
||||
|
|
@ -498,7 +498,7 @@ export default function SessionSubpanel({
|
|||
/>
|
||||
) : (
|
||||
<SearchInputV2
|
||||
tableConnections={tcFromSource(traceSource)}
|
||||
tableConnection={tcFromSource(traceSource)}
|
||||
control={control}
|
||||
name="where"
|
||||
language="lucene"
|
||||
|
|
|
|||
|
|
@ -449,7 +449,7 @@ export default function SessionsPage() {
|
|||
sqlInput={
|
||||
<Box style={{ width: '50%', flexGrow: 1 }}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(traceTrace)}
|
||||
tableConnection={tcFromSource(traceTrace)}
|
||||
onSubmit={onSubmit}
|
||||
control={control}
|
||||
name="where"
|
||||
|
|
@ -468,7 +468,7 @@ export default function SessionsPage() {
|
|||
}
|
||||
luceneInput={
|
||||
<SearchInputV2
|
||||
tableConnections={tcFromSource(traceTrace)}
|
||||
tableConnection={tcFromSource(traceTrace)}
|
||||
control={control}
|
||||
name="where"
|
||||
onLanguageChange={lang =>
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ export default function ContextSubpanel({
|
|||
sqlInput={
|
||||
originalLanguage === 'lucene' ? null : (
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(source)}
|
||||
tableConnection={tcFromSource(source)}
|
||||
control={control}
|
||||
name="where"
|
||||
placeholder="SQL WHERE clause (ex. column = 'foo')"
|
||||
|
|
@ -273,7 +273,7 @@ export default function ContextSubpanel({
|
|||
luceneInput={
|
||||
originalLanguage === 'sql' ? null : (
|
||||
<SearchInputV2
|
||||
tableConnections={tcFromSource(source)}
|
||||
tableConnection={tcFromSource(source)}
|
||||
control={control}
|
||||
name="where"
|
||||
language="lucene"
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ function ChartSeriesEditorComponent({
|
|||
}}
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName: tableName ?? '',
|
||||
connectionId: connectionId ?? '',
|
||||
|
|
@ -299,7 +299,7 @@ function ChartSeriesEditorComponent({
|
|||
<Text size="sm">Where</Text>
|
||||
{aggConditionLanguage === 'sql' ? (
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName: tableName ?? '',
|
||||
connectionId: connectionId ?? '',
|
||||
|
|
@ -316,7 +316,7 @@ function ChartSeriesEditorComponent({
|
|||
/>
|
||||
) : (
|
||||
<SearchInputV2
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
connectionId: connectionId ?? '',
|
||||
databaseName: databaseName ?? '',
|
||||
tableName: tableName ?? '',
|
||||
|
|
@ -342,7 +342,7 @@ function ChartSeriesEditorComponent({
|
|||
<div style={{ minWidth: 300 }}>
|
||||
<SQLInlineEditorControlled
|
||||
parentRef={parentRef}
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName: tableName ?? '',
|
||||
connectionId: connectionId ?? '',
|
||||
|
|
@ -732,7 +732,7 @@ export default function EditTimeChartForm({
|
|||
</Text>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(tableSource)}
|
||||
tableConnection={tcFromSource(tableSource)}
|
||||
control={control}
|
||||
name={`groupBy`}
|
||||
placeholder="SQL Columns"
|
||||
|
|
@ -808,7 +808,7 @@ export default function EditTimeChartForm({
|
|||
) : (
|
||||
<Flex gap="xs" direction="column">
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(tableSource)}
|
||||
tableConnection={tcFromSource(tableSource)}
|
||||
control={control}
|
||||
name="select"
|
||||
placeholder={
|
||||
|
|
@ -820,7 +820,7 @@ export default function EditTimeChartForm({
|
|||
/>
|
||||
{whereLanguage === 'sql' ? (
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(tableSource)}
|
||||
tableConnection={tcFromSource(tableSource)}
|
||||
control={control}
|
||||
name={`where`}
|
||||
placeholder="SQL WHERE clause (ex. column = 'foo')"
|
||||
|
|
@ -830,7 +830,7 @@ export default function EditTimeChartForm({
|
|||
/>
|
||||
) : (
|
||||
<SearchInputV2
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
connectionId: tableSource?.connection ?? '',
|
||||
databaseName: databaseName ?? '',
|
||||
tableName: tableName ?? '',
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
import cx from 'classnames';
|
||||
import {
|
||||
TableMetadata,
|
||||
tcFromChartConfig,
|
||||
tcFromSource,
|
||||
} from '@hyperdx/common-utils/dist/metadata';
|
||||
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
|
||||
|
|
@ -554,28 +555,14 @@ const DBSearchPageFiltersComponent = ({
|
|||
) => {
|
||||
return _setFilterValue(property, value, action);
|
||||
};
|
||||
const {
|
||||
toggleFilterPin,
|
||||
toggleFieldPin,
|
||||
isFilterPinned,
|
||||
isFieldPinned,
|
||||
getPinnedFields,
|
||||
} = usePinnedFilters(sourceId ?? null);
|
||||
const { toggleFilterPin, toggleFieldPin, isFilterPinned, isFieldPinned } =
|
||||
usePinnedFilters(sourceId ?? null);
|
||||
const { width, startResize } = useResizable(16, 'left');
|
||||
|
||||
const { data: countData } = useExplainQuery(chartConfig);
|
||||
const numRows: number = countData?.[0]?.rows ?? 0;
|
||||
|
||||
const { data: jsonColumns } = useJsonColumns({
|
||||
databaseName: chartConfig.from.databaseName,
|
||||
tableName: chartConfig.from.tableName,
|
||||
connectionId: chartConfig.connection,
|
||||
});
|
||||
const { data, isLoading, error } = useAllFields({
|
||||
databaseName: chartConfig.from.databaseName,
|
||||
tableName: chartConfig.from.tableName,
|
||||
connectionId: chartConfig.connection,
|
||||
});
|
||||
const { data: jsonColumns } = useJsonColumns(tcFromChartConfig(chartConfig));
|
||||
const { data, isLoading, error } = useAllFields(
|
||||
tcFromChartConfig(chartConfig),
|
||||
);
|
||||
|
||||
const { data: source } = useSource({ id: sourceId });
|
||||
const { data: tableMetadata } = useTableMetadata(tcFromSource(source));
|
||||
|
|
@ -647,7 +634,7 @@ const DBSearchPageFiltersComponent = ({
|
|||
isLoading: isFacetsLoading,
|
||||
isFetching: isFacetsFetching,
|
||||
} = useGetKeyValues({
|
||||
chartConfigs: { ...chartConfig, dateRange },
|
||||
chartConfig: { ...chartConfig, dateRange },
|
||||
limit: keyLimit,
|
||||
keys: keysToFetch,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ export default function DBTracePanel({
|
|||
</Text>
|
||||
<Flex>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={tcFromSource(parentSourceData)}
|
||||
tableConnection={tcFromSource(parentSourceData)}
|
||||
name="traceIdExpression"
|
||||
placeholder="Log Trace ID Column (ex. trace_id)"
|
||||
control={traceIdControl}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const FilterSelect: React.FC<FilterSelectProps> = ({
|
|||
dataTestId,
|
||||
}) => {
|
||||
const { data, isLoading } = useGetKeyValues({
|
||||
chartConfigs: chartConfig,
|
||||
chartConfig,
|
||||
keys: [`${metricSource.resourceAttributesExpression}['${fieldName}']`],
|
||||
});
|
||||
|
||||
|
|
@ -240,7 +240,7 @@ export const KubernetesFilters: React.FC<KubernetesFiltersProps> = ({
|
|||
dataTestId="cluster-filter-select"
|
||||
/>
|
||||
<SearchInputV2
|
||||
tableConnections={tcFromSource(metricSource)}
|
||||
tableConnection={tcFromSource(metricSource)}
|
||||
placeholder="Search query"
|
||||
language="lucene"
|
||||
name="searchQuery"
|
||||
|
|
|
|||
|
|
@ -79,19 +79,19 @@ function useMetricNames(
|
|||
}, [metricSource, dateRange]);
|
||||
|
||||
const { data: gaugeMetrics } = useGetKeyValues({
|
||||
chartConfigs: gaugeConfig,
|
||||
chartConfig: gaugeConfig,
|
||||
keys: ['MetricName'],
|
||||
limit: MAX_METRIC_NAME_OPTIONS,
|
||||
disableRowLimit: true,
|
||||
});
|
||||
const { data: histogramMetrics } = useGetKeyValues({
|
||||
chartConfigs: histogramConfig,
|
||||
chartConfig: histogramConfig,
|
||||
keys: ['MetricName'],
|
||||
limit: MAX_METRIC_NAME_OPTIONS,
|
||||
disableRowLimit: true,
|
||||
});
|
||||
const { data: sumMetrics } = useGetKeyValues({
|
||||
chartConfigs: sumConfig,
|
||||
chartConfig: sumConfig,
|
||||
keys: ['MetricName'],
|
||||
limit: MAX_METRIC_NAME_OPTIONS,
|
||||
disableRowLimit: true,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import {
|
|||
startCompletion,
|
||||
} from '@codemirror/autocomplete';
|
||||
import { sql, SQLDialect } from '@codemirror/lang-sql';
|
||||
import { Field, TableConnection } from '@hyperdx/common-utils/dist/metadata';
|
||||
import {
|
||||
Field,
|
||||
TableConnectionChoice,
|
||||
} from '@hyperdx/common-utils/dist/metadata';
|
||||
import { Paper, Text } from '@mantine/core';
|
||||
import CodeMirror, {
|
||||
Compartment,
|
||||
|
|
@ -20,7 +23,7 @@ import CodeMirror, {
|
|||
tooltips,
|
||||
} from '@uiw/react-codemirror';
|
||||
|
||||
import { useAllFields } from '@/hooks/useMetadata';
|
||||
import { useMultipleAllFields } from '@/hooks/useMetadata';
|
||||
import { useQueryHistory } from '@/utils';
|
||||
|
||||
import InputLanguageSwitch from './InputLanguageSwitch';
|
||||
|
|
@ -97,7 +100,6 @@ const AUTOCOMPLETE_LIST_FOR_SQL_FUNCTIONS = [
|
|||
const AUTOCOMPLETE_LIST_STRING = ` ${AUTOCOMPLETE_LIST_FOR_SQL_FUNCTIONS.join(' ')}`;
|
||||
|
||||
type SQLInlineEditorProps = {
|
||||
tableConnections?: TableConnection | TableConnection[];
|
||||
autoCompleteFields?: Field[];
|
||||
filterField?: (field: Field) => boolean;
|
||||
value: string;
|
||||
|
|
@ -143,6 +145,7 @@ const createStyleTheme = (allowMultiline: boolean = false) =>
|
|||
});
|
||||
|
||||
export default function SQLInlineEditor({
|
||||
tableConnection,
|
||||
tableConnections,
|
||||
filterField,
|
||||
onChange,
|
||||
|
|
@ -160,8 +163,11 @@ export default function SQLInlineEditor({
|
|||
queryHistoryType,
|
||||
parentRef,
|
||||
allowMultiline = false,
|
||||
}: SQLInlineEditorProps) {
|
||||
const { data: fields } = useAllFields(tableConnections ?? []);
|
||||
}: SQLInlineEditorProps & TableConnectionChoice) {
|
||||
const _tableConnections = tableConnection
|
||||
? [tableConnection]
|
||||
: tableConnections;
|
||||
const { data: fields } = useMultipleAllFields(_tableConnections ?? []);
|
||||
const filteredFields = useMemo(() => {
|
||||
return filterField ? fields?.filter(filterField) : fields;
|
||||
}, [fields, filterField]);
|
||||
|
|
@ -410,7 +416,9 @@ function SQLInlineEditorControlledComponent({
|
|||
additionalSuggestions,
|
||||
queryHistoryType,
|
||||
...props
|
||||
}: Omit<SQLInlineEditorProps, 'value' | 'onChange'> & UseControllerProps<any>) {
|
||||
}: Omit<SQLInlineEditorProps, 'value' | 'onChange'> &
|
||||
UseControllerProps<any> &
|
||||
TableConnectionChoice) {
|
||||
const { field, fieldState } = useController(props);
|
||||
|
||||
// Guard against wrongly typed values
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="DateTime column or expression that is part of your table's primary key."
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -180,7 +180,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="Default columns selected in search results (this can be customized per search later)"
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -225,7 +225,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
<Divider />
|
||||
<FormRow label={'Service Name Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -237,7 +237,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Log Level Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -249,7 +249,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Body Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -261,7 +261,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Log Attributes Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -273,7 +273,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Resource Attributes Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -288,7 +288,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="This DateTime column is used to display search results."
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -314,7 +314,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
|
||||
<FormRow label={'Trace Id Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -326,7 +326,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Span Id Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -343,7 +343,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="Unique identifier for a given row, will be primary key if not specified. Used for showing full row details in search results."
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -355,7 +355,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow> */}
|
||||
{/* <FormRow label={'Table Filter Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -370,7 +370,7 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="Column used for full text search if no property is specified in a Lucene-based search. Typically the message body of a log."
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -397,7 +397,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="DateTime column or expression defines the start of the span"
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -413,7 +413,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="Default columns selected in search results (this can be customized per search later)"
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -426,7 +426,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
<Divider />
|
||||
<FormRow label={'Duration Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -464,7 +464,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Trace Id Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -476,7 +476,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Span Id Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -488,7 +488,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Parent Span Id Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -500,7 +500,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Span Name Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -512,7 +512,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Span Kind Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -543,7 +543,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Status Code Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -555,7 +555,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Status Message Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -567,7 +567,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Service Name Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -579,7 +579,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Resource Attributes Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -591,7 +591,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
</FormRow>
|
||||
<FormRow label={'Event Attributes Expression'}>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -606,7 +606,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="Expression to extract span events. Used to capture events associated with spans. Expected to be Nested ( Timestamp DateTime64(9), Name LowCardinality(String), Attributes Map(LowCardinality(String), String)"
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
@ -621,7 +621,7 @@ export function TraceTableModelForm({ control, watch }: TableModelProps) {
|
|||
helpText="Column used for full text search if no property is specified in a Lucene-based search. Typically the message body of a log."
|
||||
>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnections={{
|
||||
tableConnection={{
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ import { renderHook } from '@testing-library/react';
|
|||
|
||||
import { LuceneLanguageFormatter } from '../../SearchInputV2';
|
||||
import { useAutoCompleteOptions } from '../useAutoCompleteOptions';
|
||||
import { useAllFields, useGetKeyValues, useJsonColumns } from '../useMetadata';
|
||||
import {
|
||||
useJsonColumns,
|
||||
useMultipleAllFields,
|
||||
useMultipleGetKeyValues,
|
||||
} from '../useMetadata';
|
||||
|
||||
if (!globalThis.structuredClone) {
|
||||
globalThis.structuredClone = (obj: any) => {
|
||||
|
|
@ -15,8 +19,8 @@ if (!globalThis.structuredClone) {
|
|||
// Mock dependencies
|
||||
jest.mock('../useMetadata', () => ({
|
||||
...jest.requireActual('../useMetadata.tsx'),
|
||||
useAllFields: jest.fn(),
|
||||
useGetKeyValues: jest.fn(),
|
||||
useMultipleAllFields: jest.fn(),
|
||||
useMultipleGetKeyValues: jest.fn(),
|
||||
useJsonColumns: jest.fn(),
|
||||
}));
|
||||
|
||||
|
|
@ -40,13 +44,11 @@ const mockFields: Field[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const mockTableConnections = [
|
||||
{
|
||||
databaseName: 'test_db',
|
||||
tableName: 'traces',
|
||||
connectionId: 'conn1',
|
||||
},
|
||||
];
|
||||
const mockTableConnection = {
|
||||
databaseName: 'test_db',
|
||||
tableName: 'traces',
|
||||
connectionId: 'conn1',
|
||||
};
|
||||
|
||||
describe('useAutoCompleteOptions', () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -54,11 +56,11 @@ describe('useAutoCompleteOptions', () => {
|
|||
jest.clearAllMocks();
|
||||
|
||||
// Setup default mock implementations
|
||||
(useAllFields as jest.Mock).mockReturnValue({
|
||||
(useMultipleAllFields as jest.Mock).mockReturnValue({
|
||||
data: mockFields,
|
||||
});
|
||||
|
||||
(useGetKeyValues as jest.Mock).mockReturnValue({
|
||||
(useMultipleGetKeyValues as jest.Mock).mockReturnValue({
|
||||
data: null,
|
||||
});
|
||||
|
||||
|
|
@ -70,7 +72,7 @@ describe('useAutoCompleteOptions', () => {
|
|||
it('should return field options with correct lucene formatting', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAutoCompleteOptions(luceneFormatter, 'ResourceAttributes', {
|
||||
tableConnections: mockTableConnections,
|
||||
tableConnection: mockTableConnection,
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -98,7 +100,7 @@ describe('useAutoCompleteOptions', () => {
|
|||
},
|
||||
];
|
||||
|
||||
(useGetKeyValues as jest.Mock).mockReturnValue({
|
||||
(useMultipleGetKeyValues as jest.Mock).mockReturnValue({
|
||||
data: mockKeyValues,
|
||||
});
|
||||
|
||||
|
|
@ -107,7 +109,7 @@ describe('useAutoCompleteOptions', () => {
|
|||
luceneFormatter,
|
||||
'ResourceAttributes.service.name',
|
||||
{
|
||||
tableConnections: mockTableConnections,
|
||||
tableConnection: mockTableConnection,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
@ -150,13 +152,13 @@ describe('useAutoCompleteOptions', () => {
|
|||
},
|
||||
];
|
||||
|
||||
(useGetKeyValues as jest.Mock).mockReturnValue({
|
||||
(useMultipleGetKeyValues as jest.Mock).mockReturnValue({
|
||||
data: mockKeyValues,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAutoCompleteOptions(luceneFormatter, 'ResourceAttributes', {
|
||||
tableConnections: mockTableConnections,
|
||||
tableConnection: mockTableConnection,
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -188,7 +190,7 @@ describe('useAutoCompleteOptions', () => {
|
|||
it('should handle additional suggestions', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAutoCompleteOptions(luceneFormatter, 'ResourceAttributes', {
|
||||
tableConnections: mockTableConnections,
|
||||
tableConnection: mockTableConnection,
|
||||
additionalSuggestions: ['custom.field'],
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
|
|||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
|
||||
import { deduplicate2dArray, useGetKeyValues } from '../useMetadata';
|
||||
import {
|
||||
deduplicate2dArray,
|
||||
useGetKeyValues,
|
||||
useMultipleGetKeyValues,
|
||||
} from '../useMetadata';
|
||||
|
||||
// Create a mock ChartConfig based on the Zod schema
|
||||
const createMockChartConfig = (
|
||||
|
|
@ -69,7 +73,7 @@ describe('useGetKeyValues', () => {
|
|||
const { result } = renderHook(
|
||||
() =>
|
||||
useGetKeyValues({
|
||||
chartConfigs: mockChartConfig,
|
||||
chartConfig: mockChartConfig,
|
||||
keys: mockKeys,
|
||||
}),
|
||||
{ wrapper },
|
||||
|
|
@ -118,7 +122,7 @@ describe('useGetKeyValues', () => {
|
|||
// Act
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useGetKeyValues({
|
||||
useMultipleGetKeyValues({
|
||||
chartConfigs: mockChartConfigs,
|
||||
keys: mockKeys,
|
||||
}),
|
||||
|
|
@ -150,7 +154,7 @@ describe('useGetKeyValues', () => {
|
|||
const { result } = renderHook(
|
||||
() =>
|
||||
useGetKeyValues({
|
||||
chartConfigs: mockChartConfig,
|
||||
chartConfig: mockChartConfig,
|
||||
keys: [],
|
||||
}),
|
||||
{ wrapper },
|
||||
|
|
@ -180,7 +184,7 @@ describe('useGetKeyValues', () => {
|
|||
const { result } = renderHook(
|
||||
() =>
|
||||
useGetKeyValues({
|
||||
chartConfigs: mockChartConfig,
|
||||
chartConfig: mockChartConfig,
|
||||
keys: mockKeys,
|
||||
limit: 50,
|
||||
disableRowLimit: true,
|
||||
|
|
@ -206,7 +210,7 @@ describe('useGetKeyValues', () => {
|
|||
const { result } = renderHook(
|
||||
() =>
|
||||
useGetKeyValues({
|
||||
chartConfigs: mockChartConfig,
|
||||
chartConfig: mockChartConfig,
|
||||
keys: mockKeys,
|
||||
}),
|
||||
{ wrapper },
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
|
|||
|
||||
import {
|
||||
deduplicate2dArray,
|
||||
useAllFields,
|
||||
useGetKeyValues,
|
||||
useJsonColumns,
|
||||
useMultipleAllFields,
|
||||
useMultipleGetKeyValues,
|
||||
} from '@/hooks/useMetadata';
|
||||
import { mergePath, toArray } from '@/utils';
|
||||
|
||||
|
|
@ -23,15 +23,21 @@ export function useAutoCompleteOptions(
|
|||
formatter: ILanguageFormatter,
|
||||
value: string,
|
||||
{
|
||||
tableConnections,
|
||||
tableConnection,
|
||||
additionalSuggestions,
|
||||
}: {
|
||||
tableConnections?: TableConnection | TableConnection[];
|
||||
tableConnection?: TableConnection | TableConnection[];
|
||||
additionalSuggestions?: string[];
|
||||
},
|
||||
) {
|
||||
// Fetch and gather all field options
|
||||
const { data: fields } = useAllFields(tableConnections ?? []);
|
||||
const { data: fields } = useMultipleAllFields(
|
||||
tableConnection
|
||||
? Array.isArray(tableConnection)
|
||||
? tableConnection
|
||||
: [tableConnection]
|
||||
: [],
|
||||
);
|
||||
const { fieldCompleteOptions, fieldCompleteMap } = useMemo(() => {
|
||||
const _columns = (fields ?? []).filter(c => c.jsType !== null);
|
||||
|
||||
|
|
@ -72,11 +78,11 @@ export function useAutoCompleteOptions(
|
|||
setSearchField(null);
|
||||
}
|
||||
}, [searchField, setSearchField, value, formatter]);
|
||||
const tcForJson = Array.isArray(tableConnections)
|
||||
? tableConnections.length > 0
|
||||
? tableConnections[0]
|
||||
const tcForJson = Array.isArray(tableConnection)
|
||||
? tableConnection.length > 0
|
||||
? tableConnection[0]
|
||||
: undefined
|
||||
: tableConnections;
|
||||
: tableConnection;
|
||||
const { data: jsonColumns } = useJsonColumns(
|
||||
tcForJson ?? {
|
||||
tableName: '',
|
||||
|
|
@ -93,22 +99,22 @@ export function useAutoCompleteOptions(
|
|||
);
|
||||
|
||||
// hooks to get key values
|
||||
const chartConfigs: ChartConfigWithDateRange[] = toArray(
|
||||
tableConnections,
|
||||
).map(({ databaseName, tableName, connectionId }) => ({
|
||||
connection: connectionId,
|
||||
from: {
|
||||
databaseName,
|
||||
tableName,
|
||||
},
|
||||
timestampValueExpression: '',
|
||||
select: '',
|
||||
where: '',
|
||||
// TODO: Pull in date for query as arg
|
||||
// just assuming 1/2 day is okay to query over right now
|
||||
dateRange: [new Date(NOW - (86400 * 1000) / 2), new Date(NOW)],
|
||||
}));
|
||||
const { data: keyVals } = useGetKeyValues({
|
||||
const chartConfigs: ChartConfigWithDateRange[] = toArray(tableConnection).map(
|
||||
({ databaseName, tableName, connectionId }) => ({
|
||||
connection: connectionId,
|
||||
from: {
|
||||
databaseName,
|
||||
tableName,
|
||||
},
|
||||
timestampValueExpression: '',
|
||||
select: '',
|
||||
where: '',
|
||||
// TODO: Pull in date for query as arg
|
||||
// just assuming 1/2 day is okay to query over right now
|
||||
dateRange: [new Date(NOW - (86400 * 1000) / 2), new Date(NOW)],
|
||||
}),
|
||||
);
|
||||
const { data: keyVals } = useMultipleGetKeyValues({
|
||||
chartConfigs,
|
||||
keys: searchKeys,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -68,41 +68,39 @@ export function useColumns(
|
|||
}
|
||||
|
||||
export function useJsonColumns(
|
||||
{ databaseName, tableName, connectionId }: TableConnection,
|
||||
tableConnection: TableConnection | undefined,
|
||||
options?: Partial<UseQueryOptions<string[]>>,
|
||||
) {
|
||||
const metadata = useMetadataWithSettings();
|
||||
return useQuery<string[]>({
|
||||
queryKey: ['useMetadata.useJsonColumns', { databaseName, tableName }],
|
||||
queryKey: ['useMetadata.useJsonColumns', tableConnection],
|
||||
queryFn: async () => {
|
||||
const columns = await metadata.getColumns({
|
||||
databaseName,
|
||||
tableName,
|
||||
connectionId,
|
||||
});
|
||||
if (!tableConnection) return [];
|
||||
const columns = await metadata.getColumns(tableConnection);
|
||||
return (
|
||||
filterColumnMetaByType(columns, [JSDataType.JSON])?.map(
|
||||
column => column.name,
|
||||
) ?? []
|
||||
);
|
||||
},
|
||||
enabled: !!databaseName && !!tableName && !!connectionId,
|
||||
enabled:
|
||||
tableConnection &&
|
||||
!!tableConnection.databaseName &&
|
||||
!!tableConnection.tableName &&
|
||||
!!tableConnection.connectionId,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAllFields(
|
||||
_tableConnections: TableConnection | TableConnection[],
|
||||
export function useMultipleAllFields(
|
||||
tableConnections: TableConnection[],
|
||||
options?: Partial<UseQueryOptions<Field[]>>,
|
||||
) {
|
||||
const tableConnections = Array.isArray(_tableConnections)
|
||||
? _tableConnections
|
||||
: [_tableConnections];
|
||||
const metadata = useMetadataWithSettings();
|
||||
const { data: me, isFetched } = api.useMe();
|
||||
return useQuery<Field[]>({
|
||||
queryKey: [
|
||||
'useMetadata.useAllFields',
|
||||
'useMetadata.useMultipleAllFields',
|
||||
...tableConnections.map(tc => ({ ...tc })),
|
||||
],
|
||||
queryFn: async () => {
|
||||
|
|
@ -130,6 +128,16 @@ export function useAllFields(
|
|||
});
|
||||
}
|
||||
|
||||
export function useAllFields(
|
||||
tableConnection: TableConnection | undefined,
|
||||
options?: Partial<UseQueryOptions<Field[]>>,
|
||||
) {
|
||||
return useMultipleAllFields(
|
||||
tableConnection ? [tableConnection] : [],
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
export function useTableMetadata(
|
||||
{
|
||||
databaseName,
|
||||
|
|
@ -158,7 +166,7 @@ export function useTableMetadata(
|
|||
});
|
||||
}
|
||||
|
||||
export function useGetKeyValues(
|
||||
export function useMultipleGetKeyValues(
|
||||
{
|
||||
chartConfigs,
|
||||
keys,
|
||||
|
|
@ -202,6 +210,31 @@ export function useGetKeyValues(
|
|||
});
|
||||
}
|
||||
|
||||
export function useGetKeyValues(
|
||||
{
|
||||
chartConfig,
|
||||
keys,
|
||||
limit,
|
||||
disableRowLimit,
|
||||
}: {
|
||||
chartConfig?: ChartConfigWithDateRange;
|
||||
keys: string[];
|
||||
limit?: number;
|
||||
disableRowLimit?: boolean;
|
||||
},
|
||||
options?: Omit<UseQueryOptions<any, Error>, 'queryKey'>,
|
||||
) {
|
||||
return useMultipleGetKeyValues(
|
||||
{
|
||||
chartConfigs: chartConfig ? [chartConfig] : [],
|
||||
keys,
|
||||
limit,
|
||||
disableRowLimit,
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
export function deduplicateArray<T extends object>(array: T[]): T[] {
|
||||
return deduplicate2dArray([array]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -677,6 +677,16 @@ export type TableConnection = {
|
|||
metricName?: string;
|
||||
};
|
||||
|
||||
export type TableConnectionChoice =
|
||||
| {
|
||||
tableConnection?: never;
|
||||
tableConnections?: TableConnection[];
|
||||
}
|
||||
| {
|
||||
tableConnection?: TableConnection;
|
||||
tableConnections?: never;
|
||||
};
|
||||
|
||||
export function tcFromChartConfig(config?: ChartConfig): TableConnection {
|
||||
return {
|
||||
databaseName: config?.from?.databaseName ?? '',
|
||||
|
|
|
|||
Loading…
Reference in a new issue