Skip to content

Commit 9007b32

Browse files
authored
feat(tracemetrics): Use metric type to expose applicable aggregates (#101191)
The metric types we support are currently counter, distribution, and gauge. When the metric is selected, the applicable options are presented for aggregates. Updates the context to serialize the `aggregateFields` to the URL and adds a new component that renders a static list of options for the user to select. When a selection is made, we update the yAxis for that visualize context.
1 parent dce91fa commit 9007b32

File tree

6 files changed

+170
-25
lines changed

6 files changed

+170
-25
lines changed

static/app/views/explore/metrics/metricQuery.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import type {Sort} from 'sentry/utils/discover/fields';
22
import {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode';
33
import type {AggregateField} from 'sentry/views/explore/queryParams/aggregateField';
4+
import {isGroupBy, type GroupBy} from 'sentry/views/explore/queryParams/groupBy';
45
import {ReadableQueryParams} from 'sentry/views/explore/queryParams/readableQueryParams';
5-
import {VisualizeFunction} from 'sentry/views/explore/queryParams/visualize';
6+
import {
7+
isBaseVisualize,
8+
isVisualize,
9+
Visualize,
10+
VisualizeFunction,
11+
type BaseVisualize,
12+
} from 'sentry/views/explore/queryParams/visualize';
613
import {ChartType} from 'sentry/views/insights/common/components/chart';
714

815
export interface TraceMetric {
@@ -37,6 +44,23 @@ export function decodeMetricsQueryParams(value: string): BaseMetricQuery | null
3744
return null;
3845
}
3946

47+
const rawAggregateFields = json.aggregateFields;
48+
if (!Array.isArray(rawAggregateFields)) {
49+
return null;
50+
}
51+
52+
const visualizes = rawAggregateFields
53+
.filter<BaseVisualize>(isBaseVisualize)
54+
.flatMap(vis => Visualize.fromJSON(vis));
55+
56+
if (!visualizes.length) {
57+
return null;
58+
}
59+
60+
const groupBys = rawAggregateFields.filter<GroupBy>(isGroupBy);
61+
62+
const aggregateFields = [...visualizes, ...groupBys];
63+
4064
return {
4165
metric: {name: metric},
4266
queryParams: new ReadableQueryParams({
@@ -49,7 +73,7 @@ export function decodeMetricsQueryParams(value: string): BaseMetricQuery | null
4973
sortBys: defaultSortBys(),
5074

5175
aggregateCursor: '',
52-
aggregateFields: defaultAggregateFields(),
76+
aggregateFields,
5377
aggregateSortBys: defaultAggregateSortBys(),
5478
}),
5579
};
@@ -59,6 +83,14 @@ export function encodeMetricQueryParams(metricQuery: BaseMetricQuery): string {
5983
return JSON.stringify({
6084
metric: metricQuery.metric.name,
6185
query: metricQuery.queryParams.query,
86+
aggregateFields: metricQuery.queryParams.aggregateFields.map(field => {
87+
if (isVisualize(field)) {
88+
return field.serialize();
89+
}
90+
91+
// Keep Group By as-is
92+
return field;
93+
}),
6294
});
6395
}
6496

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {CompactSelect} from 'sentry/components/core/compactSelect';
2+
import {defined} from 'sentry/utils';
3+
import {
4+
useMetricVisualize,
5+
useSetMetricVisualize,
6+
} from 'sentry/views/explore/metrics/metricsQueryParams';
7+
8+
const OPTIONS_BY_TYPE: Record<string, Array<{label: string; value: string}>> = {
9+
counter: [
10+
{
11+
label: 'sum',
12+
value: 'sum',
13+
},
14+
],
15+
distribution: [
16+
{
17+
label: 'p50',
18+
value: 'p50',
19+
},
20+
{
21+
label: 'p75',
22+
value: 'p75',
23+
},
24+
{
25+
label: 'p90',
26+
value: 'p90',
27+
},
28+
{
29+
label: 'p95',
30+
value: 'p95',
31+
},
32+
{
33+
label: 'p99',
34+
value: 'p99',
35+
},
36+
{
37+
label: 'avg',
38+
value: 'avg',
39+
},
40+
{
41+
label: 'min',
42+
value: 'min',
43+
},
44+
{
45+
label: 'max',
46+
value: 'max',
47+
},
48+
{
49+
label: 'sum',
50+
value: 'sum',
51+
},
52+
{
53+
label: 'count',
54+
value: 'count',
55+
},
56+
],
57+
gauge: [
58+
{
59+
label: 'min',
60+
value: 'min',
61+
},
62+
{
63+
label: 'max',
64+
value: 'max',
65+
},
66+
{
67+
label: 'avg',
68+
value: 'avg',
69+
},
70+
{
71+
label: 'last',
72+
value: 'last',
73+
},
74+
],
75+
};
76+
77+
export function AggregateDropdown({type}: {type: string | undefined}) {
78+
const visualize = useMetricVisualize();
79+
const setVisualize = useSetMetricVisualize();
80+
81+
return (
82+
<CompactSelect
83+
options={
84+
defined(type) && defined(OPTIONS_BY_TYPE[type]) ? OPTIONS_BY_TYPE[type] : []
85+
}
86+
value={visualize.parsedFunction?.name ?? ''}
87+
onChange={option => {
88+
setVisualize(
89+
visualize.replace({
90+
yAxis: `${option.value}(${visualize.parsedFunction?.arguments?.[0] ?? ''})`,
91+
})
92+
);
93+
}}
94+
/>
95+
);
96+
}

static/app/views/explore/metrics/metricRow.tsx renamed to static/app/views/explore/metrics/metricRow/index.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
} from 'sentry/views/explore/components/traceItemSearchQueryBuilder';
1212
import {useMetricOptions} from 'sentry/views/explore/hooks/useMetricOptions';
1313
import {type TraceMetric} from 'sentry/views/explore/metrics/metricQuery';
14+
import {AggregateDropdown} from 'sentry/views/explore/metrics/metricRow/aggregateDropdown';
1415
import {
1516
useMetricVisualize,
1617
useSetMetricName,
@@ -20,7 +21,6 @@ import {
2021
useQueryParamsQuery,
2122
useSetQueryParamsQuery,
2223
} from 'sentry/views/explore/queryParams/context';
23-
import type {VisualizeFunction} from 'sentry/views/explore/queryParams/visualize';
2424
import {TraceItemDataset} from 'sentry/views/explore/types';
2525

2626
interface MetricRowProps {
@@ -67,7 +67,7 @@ function MetricToolbar({
6767
tracesItemSearchQueryBuilderProps,
6868
traceMetric,
6969
}: MetricToolbarProps) {
70-
const visualize = useMetricVisualize() as VisualizeFunction;
70+
const visualize = useMetricVisualize();
7171
const groupBys = useQueryParamsGroupBys();
7272
const query = useQueryParamsQuery();
7373
const {data: metricOptionsData} = useMetricOptions();
@@ -78,20 +78,29 @@ function MetricToolbar({
7878
...(metricOptionsData?.data?.map(option => ({
7979
label: `${option['metric.name']} (${option['metric.type']})`,
8080
value: option['metric.name'],
81+
type: option['metric.type'],
8182
})) ?? []),
8283
// TODO(nar): Remove these when we actually have metrics served
8384
// This is only used for providing an option to test current selection behavior
8485
{
85-
label: 'test-metric',
86-
value: 'test-metric',
86+
label: 'test-distribution',
87+
value: 'test-distribution',
88+
type: 'distribution' as const,
8789
},
8890
{
89-
label: 'mock-metric',
90-
value: 'mock-metric',
91+
label: 'test-gauge',
92+
value: 'test-gauge',
93+
type: 'gauge' as const,
9194
},
9295
];
9396
}, [metricOptionsData]);
9497

98+
// TODO(nar): This should come from the metric data context
99+
// so we can display different types with conflicting names
100+
const currentMetricType = useMemo(() => {
101+
return metricOptions?.find(metricData => metricData.value === traceMetric.name)?.type;
102+
}, [metricOptions, traceMetric.name]);
103+
95104
return (
96105
<div style={{width: '100%'}}>
97106
{traceMetric.name}/{visualize.yAxis}/ by {groupBys.join(',')}/ where {query}
@@ -104,15 +113,7 @@ function MetricToolbar({
104113
setMetricName(option.value);
105114
}}
106115
/>
107-
<CompactSelect
108-
options={[
109-
{
110-
label: 'count',
111-
value: 'count',
112-
},
113-
]}
114-
value={visualize.parsedFunction?.name}
115-
/>
116+
<AggregateDropdown type={currentMetricType} />
116117
{t('by')}
117118
<CompactSelect options={[]} value={groupBys[0] ?? ''} />
118119
{t('where')}

static/app/views/explore/metrics/metricsQueryParams.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@ import type {AggregateField} from 'sentry/views/explore/queryParams/aggregateFie
88
import {
99
QueryParamsContextProvider,
1010
useQueryParamsVisualizes,
11+
useSetQueryParamsVisualizes,
1112
} from 'sentry/views/explore/queryParams/context';
1213
import {isGroupBy} from 'sentry/views/explore/queryParams/groupBy';
1314
import {ReadableQueryParams} from 'sentry/views/explore/queryParams/readableQueryParams';
14-
import {parseVisualize} from 'sentry/views/explore/queryParams/visualize';
15+
import {
16+
isVisualizeFunction,
17+
parseVisualize,
18+
VisualizeFunction,
19+
} from 'sentry/views/explore/queryParams/visualize';
1520
import type {WritableQueryParams} from 'sentry/views/explore/queryParams/writableQueryParams';
1621

1722
interface MetricNameContextValue {
@@ -40,6 +45,7 @@ export function MetricsQueryParamsProvider({
4045
(writableQueryParams: WritableQueryParams) => {
4146
const newQueryParams = updateQueryParams(queryParams, {
4247
query: getUpdatedValue(writableQueryParams.query, defaultQuery),
48+
aggregateFields: writableQueryParams.aggregateFields,
4349
});
4450

4551
setQueryParams(newQueryParams);
@@ -83,10 +89,10 @@ function getUpdatedValue<T>(
8389
return undefined;
8490
}
8591

86-
export function useMetricVisualize() {
92+
export function useMetricVisualize(): VisualizeFunction {
8793
const visualizes = useQueryParamsVisualizes();
88-
if (visualizes.length === 1) {
89-
return visualizes[0]!;
94+
if (visualizes.length === 1 && isVisualizeFunction(visualizes[0]!)) {
95+
return visualizes[0];
9096
}
9197
throw new Error('Only 1 visualize per metric allowed');
9298
}
@@ -96,6 +102,17 @@ export function useSetMetricName() {
96102
return setMetricName;
97103
}
98104

105+
export function useSetMetricVisualize() {
106+
const setVisualizes = useSetQueryParamsVisualizes();
107+
const setVisualize = useCallback(
108+
(newVisualize: VisualizeFunction) => {
109+
setVisualizes([newVisualize.serialize()]);
110+
},
111+
[setVisualizes]
112+
);
113+
return setVisualize;
114+
}
115+
99116
function updateQueryParams(
100117
readableQueryParams: ReadableQueryParams,
101118
writableQueryParams: WritableQueryParams

static/app/views/explore/metrics/multiMetricsQueryParams.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import type {ReactNode} from 'react';
2-
import {useMemo} from 'react';
1+
import {useMemo, type ReactNode} from 'react';
32
import type {Location} from 'history';
43

54
import {defined} from 'sentry/utils';

static/app/views/explore/queryParams/visualize.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class VisualizeFunction extends Visualize {
8585
this.parsedFunction = parseFunction(yAxis);
8686
}
8787

88-
clone(): Visualize {
88+
clone(): VisualizeFunction {
8989
return new VisualizeFunction(this.yAxis, {
9090
chartType: this.selectedChartType,
9191
visible: this.visible,
@@ -100,7 +100,7 @@ export class VisualizeFunction extends Visualize {
100100
chartType?: ChartType;
101101
visible?: boolean;
102102
yAxis?: string;
103-
}): Visualize {
103+
}): VisualizeFunction {
104104
return new VisualizeFunction(yAxis ?? this.yAxis, {
105105
chartType: chartType ?? this.selectedChartType,
106106
visible: visible ?? this.visible,

0 commit comments

Comments
 (0)