Skip to content

Commit a0dbe19

Browse files
authored
fix(timeseries): Handle orderbys not in groupby (#103547)
- If a field is in the orderby but not in the groupbys we currently error in snuba, but this updates the backend so we error instead so we can give the user a more friendly error message - Also fixes a bug where timeseries could be passed to the orderby, but we don't want that
1 parent bb30d49 commit a0dbe19

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

src/sentry/api/endpoints/organization_events_timeseries.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,14 @@ def get_rpc_config():
252252

253253
if top_events > 0:
254254
raw_groupby = self.get_field_list(organization, request, param_name="groupBy")
255+
raw_orderby = self.get_orderby(request)
255256
if len(raw_groupby) == 0:
256257
raise ParseError("groupBy is a required parameter when doing topEvents")
257258
if "timestamp" in raw_groupby:
258259
raise ParseError("Cannot group by timestamp")
260+
if raw_orderby:
261+
if "timestamp" in [col.strip("-") for col in raw_orderby]:
262+
raise ParseError("Cannot order by timestamp")
259263
if dataset in RPC_DATASETS:
260264
return dataset.run_top_events_timeseries_query(
261265
params=snuba_params,

src/sentry/snuba/rpc_dataset_common.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ def get_table_rpc_request(cls, query: TableQuery) -> TableRequest:
263263
stripped_orderby = orderby_column.lstrip("-")
264264
if stripped_orderby in orderby_aliases:
265265
resolved_column = orderby_aliases[stripped_orderby]
266+
# If this orderby isn't in the aliases, check if its a selected column
267+
elif stripped_orderby not in query.selected_columns:
268+
raise InvalidSearchQuery("orderby must also be in the selected columns or groupby")
266269
else:
267270
resolved_column = resolver.resolve_column(stripped_orderby)[0]
268271
resolved_orderby.append(

tests/snuba/api/endpoints/test_organization_events_timeseries_spans.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,69 @@ def test_top_events(self) -> None:
527527
"order": 2,
528528
}
529529

530+
def test_top_events_orderby_not_in_groupby(self) -> None:
531+
self.store_spans(
532+
[
533+
self.create_span(
534+
{"sentry_tags": {"transaction": "foo", "status": "success"}},
535+
start_ts=self.start + timedelta(minutes=1),
536+
duration=2001,
537+
),
538+
self.create_span(
539+
{"sentry_tags": {"transaction": "bar", "status": "success"}},
540+
start_ts=self.start + timedelta(minutes=1),
541+
duration=2000,
542+
),
543+
self.create_span(
544+
{"sentry_tags": {"transaction": "baz", "status": "success"}},
545+
start_ts=self.start + timedelta(minutes=1),
546+
duration=1999,
547+
),
548+
self.create_span(
549+
{"sentry_tags": {"transaction": "qux", "status": "success"}},
550+
start_ts=self.start + timedelta(minutes=1),
551+
duration=1998,
552+
),
553+
],
554+
is_eap=True,
555+
)
556+
557+
self.end = self.start + timedelta(minutes=6)
558+
response = self._do_request(
559+
data={
560+
"start": self.start,
561+
"end": self.end,
562+
"interval": "1m",
563+
"yAxis": "count()",
564+
"groupBy": ["transaction"],
565+
"orderby": ["-sum(span.self_time)"],
566+
"project": self.project.id,
567+
"dataset": "spans",
568+
"excludeOther": 0,
569+
"topEvents": 2,
570+
},
571+
)
572+
assert response.status_code == 400, response.content
573+
assert "orderby must also be in the selected columns or groupby" == response.data["detail"]
574+
575+
def test_top_events_orderby_is_timestamp(self) -> None:
576+
response = self._do_request(
577+
data={
578+
"start": self.start,
579+
"end": self.end,
580+
"interval": "1m",
581+
"yAxis": "count()",
582+
"groupBy": ["transaction"],
583+
"orderby": ["timestamp"],
584+
"project": self.project.id,
585+
"dataset": "spans",
586+
"excludeOther": 0,
587+
"topEvents": 2,
588+
},
589+
)
590+
assert response.status_code == 400, response.content
591+
assert "Cannot order by timestamp" == response.data["detail"]
592+
530593
def test_top_events_with_none_as_groupby(self) -> None:
531594
self.store_spans(
532595
[

0 commit comments

Comments
 (0)