Skip to content

Commit da97eca

Browse files
Zylphrexandrewshie-sentry
authored andcommitted
fix(eap): Format timestamp filters correctly as strings arent accepted (#93604)
EAP no longer accepts string values for timestamp filters. Convert them to floats. Fixes SENTRY-3WBY
1 parent 95d5389 commit da97eca

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

src/sentry/search/eap/columns.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,11 @@ def simple_measurements_field(
427427
)
428428

429429

430-
def datetime_processor(datetime_string: str) -> str:
431-
return datetime.fromisoformat(datetime_string).replace(tzinfo=tz.tzutc()).isoformat()
430+
def datetime_processor(datetime_value: str | float) -> str:
431+
if isinstance(datetime_value, float):
432+
# assumes that the timestamp is in seconds
433+
return datetime.fromtimestamp(datetime_value).replace(tzinfo=tz.tzutc()).isoformat()
434+
return datetime.fromisoformat(datetime_value).replace(tzinfo=tz.tzutc()).isoformat()
432435

433436

434437
def project_context_constructor(column_name: str) -> Callable[[SnubaParams], VirtualColumnContext]:

src/sentry/search/eap/resolver.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,12 +666,19 @@ def _resolve_search_value(
666666
if operator in constants.IN_OPERATORS:
667667
if isinstance(value, list):
668668
return AttributeValue(
669-
val_double_array=DoubleArray(values=[val for val in value])
669+
val_double_array=DoubleArray(
670+
values=[
671+
val.timestamp() if isinstance(val, datetime) else val
672+
for val in value
673+
]
674+
)
670675
)
671676
else:
672677
raise InvalidSearchQuery(
673678
f"{value} is not a valid value for doing an IN filter"
674679
)
680+
elif isinstance(value, datetime):
681+
return AttributeValue(val_double=value.timestamp())
675682
elif isinstance(value, float):
676683
return AttributeValue(val_double=value)
677684
elif column_type == constants.BOOLEAN:

src/sentry/search/eap/spans/attributes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@
210210
ResolvedAttribute(
211211
public_alias="timestamp",
212212
internal_name="sentry.timestamp",
213+
internal_type=constants.DOUBLE,
213214
search_type="string",
214215
processor=datetime_processor,
215216
),

tests/sentry/search/eap/test_spans.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
from datetime import datetime
23

34
import pytest
45
from sentry_protos.snuba.v1.endpoint_trace_item_table_pb2 import (
@@ -138,9 +139,11 @@ def test_timestamp_relative_filter(self):
138139
where, having, _ = self.resolver.resolve_query("timestamp:-24h")
139140
assert where == TraceItemFilter(
140141
comparison_filter=ComparisonFilter(
141-
key=AttributeKey(name="sentry.timestamp", type=AttributeKey.Type.TYPE_STRING),
142+
key=AttributeKey(name="sentry.timestamp", type=AttributeKey.Type.TYPE_DOUBLE),
142143
op=ComparisonFilter.OP_GREATER_THAN_OR_EQUALS,
143-
value=AttributeValue(val_str="2018-12-10 10:20:00+00:00"),
144+
value=AttributeValue(
145+
val_double=datetime.fromisoformat("2018-12-10 10:20:00+00:00").timestamp()
146+
),
144147
)
145148
)
146149
assert having is None

tests/snuba/api/endpoints/test_organization_events_span_indexed.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5515,3 +5515,65 @@ def test_file_extension(self):
55155515
assert data[0]["file_extension"] == "css"
55165516
assert data[1]["file_extension"] == "js"
55175517
assert meta["dataset"] == self.dataset
5518+
5519+
def test_filter_timestamp(self):
5520+
one_day_ago = before_now(days=1).replace(microsecond=0)
5521+
three_days_ago = before_now(days=3).replace(microsecond=0)
5522+
5523+
span1 = self.create_span({}, start_ts=one_day_ago)
5524+
span2 = self.create_span({}, start_ts=three_days_ago)
5525+
self.store_spans([span1, span2], is_eap=self.is_eap)
5526+
5527+
request = {
5528+
"field": ["timestamp"],
5529+
"project": self.project.id,
5530+
"dataset": self.dataset,
5531+
}
5532+
5533+
response = self.do_request(
5534+
{
5535+
**request,
5536+
"query": "timestamp:-2d",
5537+
}
5538+
)
5539+
assert response.status_code == 200, response.content
5540+
assert response.data["data"] == [
5541+
{
5542+
"id": span1["span_id"],
5543+
"project.name": self.project.slug,
5544+
"timestamp": one_day_ago.isoformat(),
5545+
},
5546+
]
5547+
5548+
timestamp = before_now(days=2).isoformat()
5549+
timestamp = timestamp.split("T", 2)[0]
5550+
5551+
response = self.do_request(
5552+
{
5553+
**request,
5554+
"query": f"timestamp:>{timestamp}",
5555+
}
5556+
)
5557+
assert response.status_code == 200, response.content
5558+
assert response.data["data"] == [
5559+
{
5560+
"id": span1["span_id"],
5561+
"project.name": self.project.slug,
5562+
"timestamp": one_day_ago.isoformat(),
5563+
},
5564+
]
5565+
5566+
response = self.do_request(
5567+
{
5568+
**request,
5569+
"query": f"timestamp:<{timestamp}",
5570+
}
5571+
)
5572+
assert response.status_code == 200, response.content
5573+
assert response.data["data"] == [
5574+
{
5575+
"id": span2["span_id"],
5576+
"project.name": self.project.slug,
5577+
"timestamp": three_days_ago.isoformat(),
5578+
},
5579+
]

0 commit comments

Comments
 (0)