Skip to content

Commit feda7b4

Browse files
authored
feat(logs): Use query params to write aggregate fields (#97739)
This switches the writes for aggregate fields to use query params context.
1 parent 6b16d41 commit feda7b4

File tree

16 files changed

+756
-110
lines changed

16 files changed

+756
-110
lines changed

static/app/types/utils.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,3 @@ export type DeepPartial<T> =
2020
[P in keyof T]?: DeepPartial<T[P]>;
2121
}
2222
: T;
23-
24-
export type Nullable<T> = {
25-
[P in keyof T]: T[P] | null;
26-
};

static/app/views/explore/contexts/logs/logsPageParams.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import {
3131
} from 'sentry/views/explore/contexts/logs/sortBys';
3232
import {
3333
getModeFromLocation,
34-
updateLocationWithMode,
3534
type Mode,
3635
} from 'sentry/views/explore/contexts/pageParamsContext/mode';
3736
import {OurLogKnownFieldKey} from 'sentry/views/explore/logs/types';
@@ -114,7 +113,8 @@ interface LogsPageParams {
114113
type NullablePartial<T> = {
115114
[P in keyof T]?: T[P] | null;
116115
};
117-
type LogPageParamsUpdate = NullablePartial<LogsPageParams>;
116+
type NonUpdatableParams = 'mode' | 'aggregateFn' | 'aggregateParam' | 'groupBy';
117+
type LogPageParamsUpdate = NullablePartial<Omit<LogsPageParams, NonUpdatableParams>>;
118118

119119
const [_LogsPageParamsProvider, _useLogsPageParams, LogsPageParamsContext] =
120120
createDefinedContext<LogsPageParams>({
@@ -254,10 +254,6 @@ function setLogsPageParams(location: Location, pageParams: LogPageParamsUpdate)
254254
updateNullableLocation(target, LOGS_CURSOR_KEY, pageParams.cursor);
255255
updateNullableLocation(target, LOGS_AGGREGATE_CURSOR_KEY, pageParams.aggregateCursor);
256256
updateNullableLocation(target, LOGS_FIELDS_KEY, pageParams.fields);
257-
updateNullableLocation(target, LOGS_GROUP_BY_KEY, pageParams.groupBy);
258-
updateNullableLocation(target, LOGS_AGGREGATE_FN_KEY, pageParams.aggregateFn);
259-
updateNullableLocation(target, LOGS_AGGREGATE_PARAM_KEY, pageParams.aggregateParam);
260-
updateLocationWithMode(target, pageParams.mode); // Can be swapped with updateNullableLocation if we merge page params.
261257
if (!pageParams.isTableFrozen) {
262258
updateLocationWithLogSortBys(target, pageParams.sortBys);
263259
updateLocationWithAggregateSortBys(target, pageParams.aggregateSortBys);

static/app/views/explore/contexts/pageParamsContext/visualizes.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class Visualize {
4242
chartType: ChartType;
4343
yAxis: string;
4444
stack?: string;
45-
private selectedChartType?: ChartType;
45+
selectedChartType?: ChartType;
4646

4747
constructor(yAxis: string, options?: VisualizeOptions) {
4848
this.yAxis = yAxis;

static/app/views/explore/hooks/useSaveQuery.tsx

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import {useCallback, useMemo} from 'react';
22

33
import type {DateString} from 'sentry/types/core';
4+
import {defined} from 'sentry/utils';
45
import {encodeSort} from 'sentry/utils/discover/eventView';
56
import useApi from 'sentry/utils/useApi';
67
import useOrganization from 'sentry/utils/useOrganization';
78
import usePageFilters from 'sentry/utils/usePageFilters';
89
import {useLogsPageParams} from 'sentry/views/explore/contexts/logs/logsPageParams';
910
import {useExplorePageParams} from 'sentry/views/explore/contexts/pageParamsContext';
1011
import {
11-
isGroupBy,
12-
isVisualize,
12+
isGroupBy as isLegacyGroupBy,
13+
isVisualize as isLegacyVisualize,
1314
} from 'sentry/views/explore/contexts/pageParamsContext/aggregateFields';
1415
import type {Mode} from 'sentry/views/explore/contexts/pageParamsContext/mode';
1516
import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval';
@@ -18,6 +19,10 @@ import {
1819
useInvalidateSavedQuery,
1920
type SavedQuery,
2021
} from 'sentry/views/explore/hooks/useGetSavedQueries';
22+
import {useQueryParams} from 'sentry/views/explore/queryParams/context';
23+
import {isGroupBy} from 'sentry/views/explore/queryParams/groupBy';
24+
import type {ReadableQueryParams} from 'sentry/views/explore/queryParams/readableQueryParams';
25+
import {isVisualize} from 'sentry/views/explore/queryParams/visualize';
2126

2227
// Request payload type that matches the backend ExploreSavedQuerySerializer
2328
type ExploreSavedQueryRequest = {
@@ -170,13 +175,20 @@ export function useLogsSaveQuery() {
170175
const pageFilters = usePageFilters();
171176
const [interval] = useChartInterval();
172177
const logsParams = useLogsPageParams();
178+
const queryParams = useQueryParams();
173179
const {id, title} = logsParams;
174180

175181
const {saveQueryFromSavedQuery, updateQueryFromSavedQuery} = useFromSavedQuery();
176182

177183
const requestData = useMemo((): ExploreSavedQueryRequest => {
178-
return convertLogsPageParamsToRequest(logsParams, pageFilters, interval, title ?? '');
179-
}, [logsParams, pageFilters, interval, title]);
184+
return convertLogsPageParamsToRequest({
185+
logsParams,
186+
queryParams,
187+
pageFilters,
188+
interval,
189+
title: title ?? '',
190+
});
191+
}, [logsParams, queryParams, pageFilters, interval, title]);
180192

181193
const {saveQueryApi, updateQueryApi} = useCreateOrUpdateSavedQuery(id);
182194

@@ -208,17 +220,21 @@ function convertExplorePageParamsToRequest(
208220

209221
const transformedAggregateFields = aggregateFields
210222
.filter(aggregateField => {
211-
if (isGroupBy(aggregateField)) {
223+
if (isLegacyGroupBy(aggregateField)) {
212224
return aggregateField.groupBy !== '';
213225
}
214226
return true;
215227
})
216228
.map(aggregateField => {
217-
return isVisualize(aggregateField)
218-
? {
219-
yAxes: [aggregateField.yAxis],
220-
chartType: aggregateField.chartType,
221-
}
229+
return isLegacyVisualize(aggregateField)
230+
? defined(aggregateField.selectedChartType)
231+
? {
232+
yAxes: [aggregateField.yAxis],
233+
chartType: aggregateField.selectedChartType,
234+
}
235+
: {
236+
yAxes: [aggregateField.yAxis],
237+
}
222238
: {groupBy: aggregateField.groupBy};
223239
});
224240

@@ -243,29 +259,45 @@ function convertExplorePageParamsToRequest(
243259
};
244260
}
245261

246-
function convertLogsPageParamsToRequest(
247-
logsParams: ReturnType<typeof useLogsPageParams>,
248-
pageFilters: ReturnType<typeof usePageFilters>,
249-
interval: string,
250-
title: string
251-
): ExploreSavedQueryRequest {
262+
function convertLogsPageParamsToRequest({
263+
logsParams,
264+
queryParams,
265+
pageFilters,
266+
interval,
267+
title,
268+
}: {
269+
interval: string;
270+
logsParams: ReturnType<typeof useLogsPageParams>;
271+
pageFilters: ReturnType<typeof usePageFilters>;
272+
queryParams: ReadableQueryParams;
273+
title: string;
274+
}): ExploreSavedQueryRequest {
252275
const {selection} = pageFilters;
253276
const {datetime, projects, environments} = selection;
254277
const {start, end, period} = datetime;
255278

256-
const {sortBys, fields, search, mode, groupBy, aggregateFn, aggregateParam} =
257-
logsParams;
279+
const {sortBys, fields, search, mode} = logsParams;
258280
const query = search?.formatString() ?? '';
259281

260-
const aggregate =
261-
aggregateFn && aggregateParam ? `${aggregateFn}(${aggregateParam})` : undefined;
262-
const visualize = aggregate
263-
? [
264-
{
265-
yAxes: [aggregate],
266-
},
267-
]
268-
: undefined;
282+
const aggregateFields = queryParams.aggregateFields.map(aggregateField => {
283+
if (isGroupBy(aggregateField)) {
284+
return {groupBy: aggregateField.groupBy};
285+
}
286+
287+
if (isVisualize(aggregateField)) {
288+
if (defined(aggregateField.selectedChartType)) {
289+
return {
290+
yAxes: [aggregateField.yAxis],
291+
chartType: aggregateField.selectedChartType,
292+
};
293+
}
294+
return {
295+
yAxes: [aggregateField.yAxis],
296+
};
297+
}
298+
299+
throw new Error(`Unknown aggregate field: ${JSON.stringify(aggregateField)}`);
300+
});
269301

270302
return {
271303
name: title,
@@ -281,9 +313,8 @@ function convertLogsPageParamsToRequest(
281313
fields,
282314
orderby: sortBys[0] ? encodeSort(sortBys[0]) : undefined,
283315
query,
284-
groupby: groupBy ? [groupBy] : undefined,
285316
mode,
286-
visualize,
317+
aggregateField: aggregateFields,
287318
},
288319
],
289320
};

static/app/views/explore/logs/logsQueryParams.spec.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {Mode} from 'sentry/views/explore/queryParams/mode';
66
import type {ReadableQueryParamsOptions} from 'sentry/views/explore/queryParams/readableQueryParams';
77
import {ReadableQueryParams} from 'sentry/views/explore/queryParams/readableQueryParams';
88
import {VisualizeFunction} from 'sentry/views/explore/queryParams/visualize';
9+
import {ChartType} from 'sentry/views/insights/common/components/chart';
910

1011
function locationFixture(query: Location['query']): Location {
1112
return LocationFixture({query});
@@ -201,6 +202,70 @@ describe('getReadableQueryParamsFromLocation', function () {
201202
);
202203
});
203204

205+
it('decodes aggregate fields correctly', function () {
206+
const location = locationFixture({
207+
aggregateField: [
208+
{yAxes: ['count(message)'], chartType: ChartType.AREA},
209+
{groupBy: 'message.template'},
210+
{yAxes: ['p50(foo)', 'p75(bar)']},
211+
].map(aggregateField => JSON.stringify(aggregateField)),
212+
});
213+
const queryParams = getReadableQueryParamsFromLocation(location);
214+
expect(queryParams).toEqual(
215+
new ReadableQueryParams(
216+
readableQueryParamOptions({
217+
aggregateFields: [
218+
new VisualizeFunction('count(message)', {chartType: ChartType.AREA}),
219+
{groupBy: 'message.template'},
220+
new VisualizeFunction('p50(foo)'),
221+
new VisualizeFunction('p75(bar)'),
222+
],
223+
})
224+
)
225+
);
226+
});
227+
228+
it('decodes custom aggregatefields and inserts default group bys', function () {
229+
const location = locationFixture({
230+
aggregateField: [
231+
JSON.stringify({yAxes: ['count(message)'], chartType: ChartType.LINE}),
232+
],
233+
});
234+
const queryParams = getReadableQueryParamsFromLocation(location);
235+
expect(queryParams).toEqual(
236+
new ReadableQueryParams(
237+
readableQueryParamOptions({
238+
aggregateFields: [
239+
new VisualizeFunction('count(message)', {chartType: ChartType.LINE}),
240+
{groupBy: ''},
241+
],
242+
})
243+
)
244+
);
245+
});
246+
247+
it('decodes custom aggregatefields and inserts default visualizes', function () {
248+
const location = locationFixture({
249+
aggregateField: [JSON.stringify({groupBy: 'message.template'})],
250+
});
251+
const queryParams = getReadableQueryParamsFromLocation(location);
252+
expect(queryParams.aggregateFields).toHaveLength(2);
253+
expect(queryParams.aggregateFields[0]).toEqual({groupBy: 'message.template'});
254+
expect(queryParams.aggregateFields[1]).toEqual(
255+
new VisualizeFunction('count(message)')
256+
);
257+
expect(queryParams).toEqual(
258+
new ReadableQueryParams(
259+
readableQueryParamOptions({
260+
aggregateFields: [
261+
{groupBy: 'message.template'},
262+
new VisualizeFunction('count(message)'),
263+
],
264+
})
265+
)
266+
);
267+
});
268+
204269
it('decodes custom aggregate sort bys correctly', function () {
205270
const location = locationFixture({
206271
logsGroupBy: 'severity',
@@ -218,4 +283,22 @@ describe('getReadableQueryParamsFromLocation', function () {
218283
)
219284
);
220285
});
286+
287+
it('decodes custom aggregate sort bys correctly with aggregate fields', function () {
288+
const location = locationFixture({
289+
logsAggregateSortBys: '-severity',
290+
aggregateField: [{groupBy: 'severity'}, {yAxes: ['avg(foo)']}].map(aggregateField =>
291+
JSON.stringify(aggregateField)
292+
),
293+
});
294+
const queryParams = getReadableQueryParamsFromLocation(location);
295+
expect(queryParams).toEqual(
296+
new ReadableQueryParams(
297+
readableQueryParamOptions({
298+
aggregateFields: [{groupBy: 'severity'}, new VisualizeFunction('avg(foo)')],
299+
aggregateSortBys: [{field: 'severity', kind: 'desc'}],
300+
})
301+
)
302+
);
303+
});
221304
});

0 commit comments

Comments
 (0)