diff --git a/.env b/.env index 94a302566a..7d0a5bb53a 100644 --- a/.env +++ b/.env @@ -8,8 +8,8 @@ NEXT_ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=clickhouse/clickstack-all-in-one ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-all-in-one NEXT_OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=clickhouse/clickstack-otel-collector OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-otel-collector -CODE_VERSION=2.24.1 -IMAGE_VERSION_SUB_TAG=.24.1 +CODE_VERSION=2.23.0 +IMAGE_VERSION_SUB_TAG=.23.0 IMAGE_VERSION=2 IMAGE_NIGHTLY_TAG=2-nightly IMAGE_LATEST_TAG=latest @@ -38,11 +38,5 @@ HDX_DEV_OTEL_HTTP_PORT=4318 HDX_DEV_OTEL_METRICS_PORT=8888 HDX_DEV_OTEL_JSON_HTTP_PORT=14318 -# Otel Collector version (used as Docker build arg for image tags and component versions) -# When bumping, look up the core version from the upstream manifest: -# https://github.com/open-telemetry/opentelemetry-collector-releases/blob/main/distributions/otelcol-contrib/manifest.yaml -OTEL_COLLECTOR_VERSION=0.149.0 -OTEL_COLLECTOR_CORE_VERSION=1.55.0 - # Otel/Clickhouse config HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=default diff --git a/packages/api/src/routers/external-api/v2/utils/dashboards.ts b/packages/api/src/routers/external-api/v2/utils/dashboards.ts index 504cf85cbe..9a8b1245e7 100644 --- a/packages/api/src/routers/external-api/v2/utils/dashboards.ts +++ b/packages/api/src/routers/external-api/v2/utils/dashboards.ts @@ -163,6 +163,15 @@ const convertToExternalTileChartConfig = ( sourceId: config.source, numberFormat: config.numberFormat, }; + case DisplayType.Bar: + return { + configType: 'sql', + displayType: DisplayType.Bar, + connectionId: config.connection, + sqlTemplate: config.sqlTemplate, + sourceId: config.source, + numberFormat: config.numberFormat, + }; case DisplayType.Search: case DisplayType.Markdown: case DisplayType.Heatmap: @@ -239,6 +248,16 @@ const convertToExternalTileChartConfig = ( groupBy: stringValueOrDefault(config.groupBy, undefined), numberFormat: config.numberFormat, }; + case DisplayType.Bar: + return { + displayType: config.displayType, + sourceId, + select: Array.isArray(config.select) + ? [convertToExternalSelectItem(config.select[0])] + : [DEFAULT_SELECT_ITEM], + groupBy: stringValueOrDefault(config.groupBy, undefined), + numberFormat: config.numberFormat, + }; case DisplayType.Table: return { ...pick(config, ['having', 'numberFormat', 'groupByColumnsOnLeft']), @@ -370,6 +389,7 @@ export function convertToInternalTileConfig( case 'table': case 'number': case 'pie': + case 'bar': internalConfig = { configType: 'sql', displayType: @@ -377,7 +397,9 @@ export function convertToInternalTileConfig( ? DisplayType.Table : externalConfig.displayType === 'number' ? DisplayType.Number - : DisplayType.Pie, + : externalConfig.displayType === 'pie' + ? DisplayType.Pie + : DisplayType.Bar, name, connection: externalConfig.connectionId, sqlTemplate: externalConfig.sqlTemplate, @@ -453,6 +475,16 @@ export function convertToInternalTileConfig( name, } satisfies BuilderSavedChartConfig; break; + case 'bar': + internalConfig = { + ...pick(externalConfig, ['groupBy', 'numberFormat']), + displayType: DisplayType.Bar, + select: [convertToInternalSelectItem(externalConfig.select[0])], + source: externalConfig.sourceId, + where: '', + name, + } satisfies BuilderSavedChartConfig; + break; case 'search': internalConfig = { ...pick(externalConfig, ['select', 'where']), diff --git a/packages/api/src/utils/zod.ts b/packages/api/src/utils/zod.ts index 84181e5844..dee3678272 100644 --- a/packages/api/src/utils/zod.ts +++ b/packages/api/src/utils/zod.ts @@ -281,6 +281,11 @@ const externalDashboardPieRawSqlChartConfigSchema = displayType: z.literal('pie'), }); +const externalDashboardCategoricalBarRawSqlChartConfigSchema = + externalDashboardRawSqlChartConfigBaseSchema.extend({ + displayType: z.literal('bar'), + }); + const externalDashboardNumberChartConfigSchema = z.object({ displayType: z.literal('number'), sourceId: objectIdSchema, @@ -296,6 +301,14 @@ const externalDashboardPieChartConfigSchema = z.object({ numberFormat: NumberFormatSchema.optional(), }); +const externalDashboardCategoricalBarChartConfigSchema = z.object({ + displayType: z.literal('bar'), + sourceId: objectIdSchema, + select: z.array(externalDashboardSelectItemSchema).length(1), + groupBy: z.string().max(10000).optional(), + numberFormat: NumberFormatSchema.optional(), +}); + const externalDashboardSearchChartConfigSchema = z.object({ displayType: z.literal('search'), sourceId: objectIdSchema, @@ -317,6 +330,7 @@ const externalDashboardBuilderTileConfigSchema = z.discriminatedUnion( externalDashboardTableChartConfigSchema, externalDashboardNumberChartConfigSchema, externalDashboardPieChartConfigSchema, + externalDashboardCategoricalBarChartConfigSchema, externalDashboardMarkdownChartConfigSchema, externalDashboardSearchChartConfigSchema, ], @@ -334,6 +348,7 @@ const externalDashboardRawSqlTileConfigSchema = z.discriminatedUnion( externalDashboardTableRawSqlChartConfigSchema, externalDashboardNumberRawSqlChartConfigSchema, externalDashboardPieRawSqlChartConfigSchema, + externalDashboardCategoricalBarRawSqlChartConfigSchema, ], ); diff --git a/packages/app/src/ChartUtils.tsx b/packages/app/src/ChartUtils.tsx index 20265625f6..3e3f2546dc 100644 --- a/packages/app/src/ChartUtils.tsx +++ b/packages/app/src/ChartUtils.tsx @@ -1080,6 +1080,19 @@ export function convertToPieChartConfig( return omit(config, ['granularity']); } +export function convertToBarChartConfig( + config: BuilderChartConfigWithOptTimestamp, +): BuilderChartConfigWithOptTimestamp { + const convertedConfig = structuredClone(omit(config, ['granularity'])); + + // Apply a default limit if not already configured + if (!convertedConfig.limit) { + convertedConfig.limit = { limit: 10 }; + } + + return convertedConfig; +} + export function convertToTableChartConfig( config: BuilderChartConfigWithOptTimestamp, ): BuilderChartConfigWithOptTimestamp { diff --git a/packages/app/src/DBDashboardPage.tsx b/packages/app/src/DBDashboardPage.tsx index c70cc48692..908624ae40 100644 --- a/packages/app/src/DBDashboardPage.tsx +++ b/packages/app/src/DBDashboardPage.tsx @@ -114,6 +114,7 @@ import useDashboardContainers, { import { calculateNextTilePosition, makeId } from '@/utils/tilePositioning'; import ChartContainer from './components/charts/ChartContainer'; +import { DBBarChart } from './components/DBBarChart'; import { DBPieChart } from './components/DBPieChart'; import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import OnboardingModal from './components/OnboardingModal'; @@ -658,6 +659,14 @@ const Tile = forwardRef( config={queriedConfig} /> )} + {queriedConfig?.displayType === DisplayType.Bar && ( + + )} {effectiveMarkdownConfig?.displayType === DisplayType.Markdown && 'markdown' in effectiveMarkdownConfig && ( diff --git a/packages/app/src/components/ChartDisplaySettingsDrawer.tsx b/packages/app/src/components/ChartDisplaySettingsDrawer.tsx index b5b3b5b853..db29c8cb0f 100644 --- a/packages/app/src/components/ChartDisplaySettingsDrawer.tsx +++ b/packages/app/src/components/ChartDisplaySettingsDrawer.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo } from 'react'; -import { useForm, useWatch } from 'react-hook-form'; +import { Controller, useForm, useWatch } from 'react-hook-form'; import { ChartConfigWithDateRange, DisplayType, @@ -12,6 +12,7 @@ import { Divider, Drawer, Group, + NumberInput, Stack, } from '@mantine/core'; @@ -21,6 +22,8 @@ import { FormatTime } from '@/useFormatTime'; import { CheckBoxControlled } from './InputControlled'; import { DEFAULT_NUMBER_FORMAT, NumberFormatForm } from './NumberFormat'; +export const DEFAULT_MAX_GROUPS = 10; + export type ChartConfigDisplaySettings = Pick< ChartConfigWithDateRange, | 'numberFormat' @@ -28,6 +31,7 @@ export type ChartConfigDisplaySettings = Pick< | 'fillNulls' | 'compareToPreviousPeriod' > & { + limit?: { limit?: number }; groupByColumnsOnLeft?: boolean; }; @@ -58,6 +62,7 @@ function applyDefaultSettings( : settings.alignDateRangeToGranularity, fillNulls: settings.fillNulls ?? 0, compareToPreviousPeriod: settings.compareToPreviousPeriod ?? false, + limit: settings.limit ?? { limit: DEFAULT_MAX_GROUPS }, groupByColumnsOnLeft: settings.groupByColumnsOnLeft ?? false, }; } @@ -105,6 +110,7 @@ export default function ChartDisplaySettingsDrawer({ const isTimeChart = displayType === DisplayType.Line || displayType === DisplayType.StackedBar; + const isBarChart = displayType === DisplayType.Bar && configType !== 'sql'; // Group By column ordering only applies to builder table charts; raw SQL // configs let the user author whatever column order they want directly. @@ -157,6 +163,32 @@ export default function ChartDisplaySettingsDrawer({ )} + {isBarChart && ( + <> + + ( + + field.onChange({ + limit: typeof v === 'number' ? v : DEFAULT_MAX_GROUPS, + }) + } + /> + )} + /> + + + + )} + {showGroupByColumnsOnLeft && ( <> { expect(errors.filter(e => e.path === 'series')).toHaveLength(0); }); + it('errors when Bar chart has no groupBy', () => { + const setError = jest.fn(); + const errors = validateChartForm( + makeForm({ + displayType: DisplayType.Bar, + source: 'source-log', + series: [seriesItem], + groupBy: undefined, + }), + logSource, + setError, + ); + expect(errors).toContainEqual( + expect.objectContaining({ + path: 'groupBy', + message: 'Group By is required for bar charts', + }), + ); + }); + + it('errors when Bar chart has an empty groupBy array', () => { + const setError = jest.fn(); + const errors = validateChartForm( + makeForm({ + displayType: DisplayType.Bar, + source: 'source-log', + series: [seriesItem], + groupBy: [] as any, + }), + logSource, + setError, + ); + expect(errors).toContainEqual( + expect.objectContaining({ + path: 'groupBy', + message: 'Group By is required for bar charts', + }), + ); + }); + + it('returns no errors for Bar chart with a valid groupBy', () => { + const setError = jest.fn(); + const errors = validateChartForm( + makeForm({ + displayType: DisplayType.Bar, + source: 'source-log', + series: [seriesItem], + groupBy: 'ServiceName', + }), + logSource, + setError, + ); + expect(errors.filter(e => e.path === 'groupBy')).toHaveLength(0); + }); + + it('does not require groupBy for raw SQL Bar charts', () => { + const setError = jest.fn(); + const errors = validateChartForm( + makeForm({ + configType: 'sql', + displayType: DisplayType.Bar, + sqlTemplate: 'SELECT 1', + connection: 'conn-1', + groupBy: undefined, + }), + undefined, + setError, + ); + expect(errors.filter(e => e.path === 'groupBy')).toHaveLength(0); + }); + it('does not apply single-series limit for raw SQL Number charts', () => { const setError = jest.fn(); const errors = validateChartForm( diff --git a/packages/app/src/components/ChartEditor/constants.tsx b/packages/app/src/components/ChartEditor/constants.tsx index 95287b319b..2430a91b5b 100644 --- a/packages/app/src/components/ChartEditor/constants.tsx +++ b/packages/app/src/components/ChartEditor/constants.tsx @@ -17,6 +17,16 @@ ORDER BY ts ASC;`; export const SQL_PLACEHOLDERS: Record = { [DisplayType.Line]: TIMESERIES_PLACEHOLDER_SQL, [DisplayType.StackedBar]: TIMESERIES_PLACEHOLDER_SQL, + [DisplayType.Bar]: `SELECT + SeverityText, + count() +FROM + default.otel_logs +WHERE TimestampTime >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) + AND TimestampTime < fromUnixTimestamp64Milli({endDateMilliseconds:Int64}) +GROUP BY SeverityText +ORDER BY count() DESC +LIMIT 10;`, [DisplayType.Table]: `SELECT count() FROM @@ -94,6 +104,34 @@ export const DISPLAY_TYPE_INSTRUCTIONS: Partial< > = { [DisplayType.Line]: TIMESERIES_INSTRUCTIONS, [DisplayType.StackedBar]: TIMESERIES_INSTRUCTIONS, + [DisplayType.Bar]: ( + <> + + Result columns are plotted as follows: + + + + + Bar Value + + + {' '} + — The first numeric column determines each bar's height. + + + + + X Axis Label + + + {' '} + — Each unique value of each string, map, and array type column will + be used as an X axis label. Group By is required. + + + + + ), [DisplayType.Pie]: ( <> diff --git a/packages/app/src/components/ChartEditor/utils.ts b/packages/app/src/components/ChartEditor/utils.ts index 5f539566b3..290c131230 100644 --- a/packages/app/src/components/ChartEditor/utils.ts +++ b/packages/app/src/components/ChartEditor/utils.ts @@ -58,11 +58,13 @@ export const isRawSqlDisplayType = ( | DisplayType.Table | DisplayType.Line | DisplayType.StackedBar + | DisplayType.Bar | DisplayType.Pie | DisplayType.Number => displayType === DisplayType.Table || displayType === DisplayType.Line || displayType === DisplayType.StackedBar || + displayType === DisplayType.Bar || displayType === DisplayType.Pie || displayType === DisplayType.Number; @@ -290,12 +292,13 @@ export const validateChartForm = ( } } - // Validate number and pie charts only have one series + // Validate number, pie, and bar charts only have one series if ( !isRawSqlChart && Array.isArray(form.series) && (form.displayType === DisplayType.Number || - form.displayType === DisplayType.Pie) && + form.displayType === DisplayType.Pie || + form.displayType === DisplayType.Bar) && form.series.length > 1 ) { errors.push({ @@ -304,6 +307,19 @@ export const validateChartForm = ( }); } + // Validate bar charts require a group by + if ( + !isRawSqlChart && + form.displayType === DisplayType.Bar && + (!form.groupBy || + (Array.isArray(form.groupBy) && form.groupBy.length === 0)) + ) { + errors.push({ + path: `groupBy`, + message: 'Group By is required for bar charts', + }); + } + for (const error of errors) { console.warn(`Validation error in field ${error.path}: ${error.message}`); setError(error.path, { diff --git a/packages/app/src/components/DBBarChart.tsx b/packages/app/src/components/DBBarChart.tsx new file mode 100644 index 0000000000..5eb76c5363 --- /dev/null +++ b/packages/app/src/components/DBBarChart.tsx @@ -0,0 +1,251 @@ +import { memo, useMemo } from 'react'; +import { + Bar, + BarChart, + CartesianGrid, + Cell, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from 'recharts'; +import { isBuilderChartConfig } from '@hyperdx/common-utils/dist/guards'; +import { + BuilderChartConfigWithOptTimestamp, + RawSqlConfigWithDateRange, +} from '@hyperdx/common-utils/dist/types'; +import { Flex } from '@mantine/core'; + +import { + buildMVDateRangeIndicator, + convertToBarChartConfig, + formatResponseForPieChart, +} from '@/ChartUtils'; +import { useQueriedChartConfig } from '@/hooks/useChartConfig'; +import { useMVOptimizationExplanation } from '@/hooks/useMVOptimizationExplanation'; +import { useSource } from '@/source'; +import type { NumberFormat } from '@/types'; +import { formatNumber, getColorProps, truncateMiddle } from '@/utils'; + +import ChartContainer from './charts/ChartContainer'; +import ChartErrorState, { + ChartErrorStateVariant, +} from './charts/ChartErrorState'; +import { ChartTooltipContainer, ChartTooltipItem } from './charts/ChartTooltip'; +import MVOptimizationIndicator from './MaterializedViews/MVOptimizationIndicator'; + +const BarChartTooltip = memo( + ({ + active, + payload, + numberFormat, + }: { + active?: boolean; + payload?: { + name: string; + value: number; + payload: { color: string; label: string }; + }[]; + numberFormat?: NumberFormat; + }) => { + if (!active || !payload?.length) return null; + const entry = payload[0]; + return ( + + + + ); + }, +); + +BarChartTooltip.displayName = 'BarChartTooltip'; + +const BarXAxisTick = ({ + x, + y, + payload, +}: { + x?: number; + y?: number; + payload?: { value: string }; +}) => { + const label = payload?.value ?? ''; + return ( + + + {truncateMiddle(label, 20)} + + + ); +}; + +export const DBBarChart = ({ + config, + title, + enabled = true, + queryKeyPrefix, + showMVOptimizationIndicator = true, + toolbarPrefix, + toolbarSuffix, + errorVariant, +}: { + config: BuilderChartConfigWithOptTimestamp | RawSqlConfigWithDateRange; + title?: React.ReactNode; + enabled?: boolean; + queryKeyPrefix?: string; + showMVOptimizationIndicator?: boolean; + toolbarPrefix?: React.ReactNode[]; + toolbarSuffix?: React.ReactNode[]; + errorVariant?: ChartErrorStateVariant; +}) => { + const { data: source } = useSource({ + id: config.source, + }); + + const queriedConfig = useMemo(() => { + return isBuilderChartConfig(config) + ? convertToBarChartConfig(config) + : config; + }, [config]); + + const builderQueriedConfig = isBuilderChartConfig(queriedConfig) + ? queriedConfig + : undefined; + const { data: mvOptimizationData } = + useMVOptimizationExplanation(builderQueriedConfig); + + const { data, isLoading, isError, error } = useQueriedChartConfig( + queriedConfig, + { + placeholderData: (prev: any) => prev, + queryKey: [queryKeyPrefix, queriedConfig], + enabled, + }, + ); + + const toolbarItemsMemo = useMemo(() => { + const allToolbarItems = []; + + if (toolbarPrefix && toolbarPrefix.length > 0) { + allToolbarItems.push(...toolbarPrefix); + } + + if (source && showMVOptimizationIndicator && builderQueriedConfig) { + allToolbarItems.push( + , + ); + } + + const dateRangeIndicator = buildMVDateRangeIndicator({ + mvOptimizationData, + originalDateRange: queriedConfig.dateRange, + }); + + if (dateRangeIndicator) { + allToolbarItems.push(dateRangeIndicator); + } + + if (toolbarSuffix && toolbarSuffix.length > 0) { + allToolbarItems.push(...toolbarSuffix); + } + + return allToolbarItems; + }, [ + toolbarPrefix, + toolbarSuffix, + source, + showMVOptimizationIndicator, + mvOptimizationData, + queriedConfig, + builderQueriedConfig, + ]); + + const [barChartData, responseFormatError] = useMemo(() => { + if (!data) return [[], null]; + try { + return [formatResponseForPieChart(data, getColorProps), null]; + } catch (err) { + return [[], err instanceof Error ? err : new Error(String(err))]; + } + }, [data]); + + return ( + + {isLoading && !data ? ( +
+ Loading Chart Data... +
+ ) : isError && error ? ( + + ) : responseFormatError ? ( + + ) : data?.data.length === 0 ? ( +
+ No data found within time range. +
+ ) : ( + + + + + } interval={0} /> + + config.numberFormat + ? formatNumber(v, config.numberFormat) + : String(v) + } + tick={{ fontSize: 11, fill: '#888' }} + width={60} + /> + } + /> + + {barChartData.map(entry => ( + + ))} + + + + + )} +
+ ); +}; diff --git a/packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx b/packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx index 1352583c36..afec0224ba 100644 --- a/packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx +++ b/packages/app/src/components/DBEditTimeChartForm/EditTimeChartForm.tsx @@ -185,6 +185,7 @@ export default function EditTimeChartForm({ compareToPreviousPeriod, numberFormat, groupByColumnsOnLeft, + limit, ] = useWatch({ control, name: [ @@ -193,6 +194,7 @@ export default function EditTimeChartForm({ 'compareToPreviousPeriod', 'numberFormat', 'groupByColumnsOnLeft', + 'limit', ], }); @@ -212,6 +214,7 @@ export default function EditTimeChartForm({ compareToPreviousPeriod, numberFormat, groupByColumnsOnLeft, + limit, }), [ alignDateRangeToGranularity, @@ -219,6 +222,7 @@ export default function EditTimeChartForm({ compareToPreviousPeriod, numberFormat, groupByColumnsOnLeft, + limit, ], ); @@ -462,15 +466,19 @@ export default function EditTimeChartForm({ fillNulls, compareToPreviousPeriod, groupByColumnsOnLeft, + limit, }: ChartConfigDisplaySettings) => { setValue('numberFormat', numberFormat); setValue('alignDateRangeToGranularity', alignDateRangeToGranularity); setValue('fillNulls', fillNulls); setValue('compareToPreviousPeriod', compareToPreviousPeriod); setValue('groupByColumnsOnLeft', groupByColumnsOnLeft); + if (displayType === DisplayType.Bar) { + setValue('limit', limit); + } onSubmit(); }, - [setValue, onSubmit], + [setValue, onSubmit, displayType], ); const tableConnection = useMemo( diff --git a/packages/app/src/utils/tilePositioning.ts b/packages/app/src/utils/tilePositioning.ts index 8c7ad121ea..94bd4a027b 100644 --- a/packages/app/src/utils/tilePositioning.ts +++ b/packages/app/src/utils/tilePositioning.ts @@ -73,6 +73,7 @@ export function getDefaultTileSize(displayType?: DisplayType): { switch (displayType) { case DisplayType.Line: case DisplayType.StackedBar: + case DisplayType.Bar: return { w: 12, h: 10 }; case DisplayType.Table: diff --git a/packages/app/tests/e2e/page-objects/DashboardPage.ts b/packages/app/tests/e2e/page-objects/DashboardPage.ts index 7c5424bf53..823b368a47 100644 --- a/packages/app/tests/e2e/page-objects/DashboardPage.ts +++ b/packages/app/tests/e2e/page-objects/DashboardPage.ts @@ -30,7 +30,14 @@ export type TileConfig = { groupBy?: string; markdown?: string; }; -type SeriesType = 'time' | 'number' | 'table' | 'search' | 'markdown' | 'pie'; +type SeriesType = + | 'time' + | 'number' + | 'table' + | 'search' + | 'markdown' + | 'pie' + | 'bar'; /** * Series data structure for chart verification * Supports all chart types: time, number, table, search, markdown @@ -504,7 +511,9 @@ export class DashboardPage { const type: SeriesData['type'] = config.displayType === 'line' || config.displayType === 'stacked_bar' ? 'time' - : config.displayType; + : config.displayType === 'bar' + ? 'bar' + : config.displayType; const groupBy = config.groupBy ? [config.groupBy] : undefined; const selectItems = Array.isArray(config.select) ? config.select : []; diff --git a/packages/common-utils/src/rawSqlParams.ts b/packages/common-utils/src/rawSqlParams.ts index 1062e2f3f8..5850cd9666 100644 --- a/packages/common-utils/src/rawSqlParams.ts +++ b/packages/common-utils/src/rawSqlParams.ts @@ -76,6 +76,10 @@ export const QUERY_PARAMS_BY_DISPLAY_TYPE: Record< QUERY_PARAMS.intervalSeconds, QUERY_PARAMS.intervalMilliseconds, ], + [DisplayType.Bar]: [ + QUERY_PARAMS.startDateMilliseconds, + QUERY_PARAMS.endDateMilliseconds, + ], [DisplayType.Table]: [ QUERY_PARAMS.startDateMilliseconds, QUERY_PARAMS.endDateMilliseconds, @@ -110,6 +114,7 @@ export const DATE_RANGE_WHERE_EXAMPLE_SQL = `WHERE TimestampTime >= fromUnixTime export const QUERY_PARAM_EXAMPLES: Record = { [DisplayType.Line]: TIME_CHART_EXAMPLE_SQL, [DisplayType.StackedBar]: TIME_CHART_EXAMPLE_SQL, + [DisplayType.Bar]: DATE_RANGE_WHERE_EXAMPLE_SQL, [DisplayType.Table]: DATE_RANGE_WHERE_EXAMPLE_SQL, [DisplayType.Pie]: DATE_RANGE_WHERE_EXAMPLE_SQL, [DisplayType.Number]: DATE_RANGE_WHERE_EXAMPLE_SQL, diff --git a/packages/common-utils/src/types.ts b/packages/common-utils/src/types.ts index 509ad79270..7aa9a1afe1 100644 --- a/packages/common-utils/src/types.ts +++ b/packages/common-utils/src/types.ts @@ -17,6 +17,7 @@ export const MetricsDataTypeSchema = z.nativeEnum(MetricsDataType); export enum DisplayType { Line = 'line', StackedBar = 'stacked_bar', + Bar = 'bar', Table = 'table', Pie = 'pie', Number = 'number',