Skip to content

Commit 5bea8f4

Browse files
feat: Allow id or event_id in project events endpoint (#10898)
* feat: Allow id or event_id in project events endpoint Reprise of a previous PR that failed because there is no index on event_id alone. Now I've added this functionality to the project event details endpoint where we know we always have the project id available, and I've made the fronted code use this endpoint instead of the bare /events/x/ endpoint.
1 parent 9a6c6d5 commit 5bea8f4

File tree

4 files changed

+81
-19
lines changed

4 files changed

+81
-19
lines changed

src/sentry/api/endpoints/project_event_details.py

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
from sentry.api.base import DocSection
88
from sentry.api.bases.project import ProjectEndpoint
99
from sentry.api.serializers import serialize
10-
from sentry.models import Event
10+
from sentry.models import Event, Release, UserReport
1111
from sentry.utils.apidocs import scenario, attach_scenarios
12+
from sentry.utils.validators import is_event_id
1213

1314

1415
@scenario('RetrieveEventForProject')
@@ -23,6 +24,20 @@ def retrieve_event_for_project_scenario(runner):
2324
class ProjectEventDetailsEndpoint(ProjectEndpoint):
2425
doc_section = DocSection.EVENTS
2526

27+
def _get_release_info(self, request, event):
28+
version = event.get_tag('sentry:release')
29+
if not version:
30+
return None
31+
try:
32+
release = Release.objects.get(
33+
projects=event.project,
34+
organization_id=event.project.organization_id,
35+
version=version,
36+
)
37+
except Release.DoesNotExist:
38+
return {'version': version}
39+
return serialize(release, request.user)
40+
2641
@attach_scenarios([retrieve_event_for_project_scenario])
2742
def get(self, request, project, event_id):
2843
"""
@@ -35,16 +50,33 @@ def get(self, request, project, event_id):
3550
event belongs to.
3651
:pparam string project_slug: the slug of the project the event
3752
belongs to.
38-
:pparam string event_id: the hexadecimal ID of the event to
39-
retrieve (as reported by the raven client).
53+
:pparam string event_id: the id of the event to retrieve (either the
54+
numeric primary-key or the hexadecimal id as
55+
reported by the raven client)
4056
:auth: required
4157
"""
42-
try:
43-
event = Event.objects.get(
44-
event_id=event_id,
45-
project_id=project.id,
46-
)
47-
except Event.DoesNotExist:
58+
59+
event = None
60+
# If its a numeric string, check if it's an event Primary Key first
61+
if event_id.isdigit():
62+
try:
63+
event = Event.objects.get(
64+
id=event_id,
65+
project_id=project.id,
66+
)
67+
except Event.DoesNotExist:
68+
pass
69+
# If it was not found as a PK, and its a possible event_id, search by that instead.
70+
if event is None and is_event_id(event_id):
71+
try:
72+
event = Event.objects.get(
73+
event_id=event_id,
74+
project_id=project.id,
75+
)
76+
except Event.DoesNotExist:
77+
pass
78+
79+
if event is None:
4880
return Response({'detail': 'Event not found'}, status=404)
4981

5082
Event.objects.bind_nodes([event], 'data')
@@ -72,7 +104,17 @@ def get(self, request, project, event_id):
72104
except IndexError:
73105
prev_event = None
74106

107+
try:
108+
user_report = UserReport.objects.get(
109+
event_id=event.event_id,
110+
project=event.project,
111+
)
112+
except UserReport.DoesNotExist:
113+
user_report = None
114+
75115
data = serialize(event, request.user)
116+
data['userReport'] = serialize(user_report, request.user)
117+
data['release'] = self._get_release_info(request, event)
76118

77119
if next_event:
78120
data['nextEventID'] = six.text_type(next_event.event_id)

src/sentry/api/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@
760760
name='sentry-api-0-project-events'
761761
),
762762
url(
763-
r'^projects/(?P<organization_slug>[^\/]+)/(?P<project_slug>[^\/]+)/events/(?P<event_id>[\w-]+)/$',
763+
r'^projects/(?P<organization_slug>[^\/]+)/(?P<project_slug>[^\/]+)/events/(?P<event_id>(?:\d+|[A-Fa-f0-9]{32}))/$',
764764
ProjectEventDetailsEndpoint.as_view(),
765765
name='sentry-api-0-project-event-details'
766766
),

src/sentry/static/sentry/app/views/groupEventDetails.jsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@ const GroupEventDetails = createReactClass({
3838
},
3939

4040
fetchData() {
41-
let eventId = this.props.params.eventId || 'latest';
41+
const eventId = this.props.params.eventId || 'latest';
42+
const groupId = this.getGroup().id;
43+
const orgSlug = this.getOrganization().slug;
44+
const projSlug = this.getProject().slug;
4245

4346
let url =
4447
eventId === 'latest' || eventId === 'oldest'
45-
? '/issues/' + this.getGroup().id + '/events/' + eventId + '/'
46-
: '/events/' + eventId + '/';
48+
? `/issues/${groupId}/events/${eventId}/`
49+
: `/projects/${orgSlug}/${projSlug}/events/${eventId}/`;
4750

4851
this.setState({
4952
loading: true,
@@ -59,9 +62,9 @@ const GroupEventDetails = createReactClass({
5962
});
6063

6164
this.api.bulkUpdate({
62-
orgId: this.getOrganization().slug,
63-
projectId: this.getProject().slug,
64-
itemIds: [this.getGroup().id],
65+
orgId: orgSlug,
66+
projectId: projSlug,
67+
itemIds: [groupId],
6568
failSilently: true,
6669
data: {hasSeen: true},
6770
});

tests/sentry/api/endpoints/test_project_event_details.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ def test_simple(self):
1414

1515
group = self.create_group()
1616
prev_event = self.create_event(
17-
event_id='a',
17+
event_id='a' * 32,
1818
group=group,
1919
datetime=datetime(2013, 8, 13, 3, 8, 24),
2020
)
2121
cur_event = self.create_event(
22-
event_id='b',
22+
event_id='b' * 32,
2323
group=group,
2424
datetime=datetime(2013, 8, 13, 3, 8, 25),
2525
)
2626
next_event = self.create_event(
27-
event_id='c',
27+
event_id='c' * 32,
2828
group=group,
2929
datetime=datetime(2013, 8, 13, 3, 8, 26),
3030
)
@@ -44,3 +44,20 @@ def test_simple(self):
4444
assert response.data['nextEventID'] == six.text_type(next_event.event_id)
4545
assert response.data['previousEventID'] == six.text_type(prev_event.event_id)
4646
assert response.data['groupID'] == six.text_type(group.id)
47+
48+
# Same event can be looked up by primary key
49+
url = reverse(
50+
'sentry-api-0-project-event-details',
51+
kwargs={
52+
'event_id': cur_event.id,
53+
'project_slug': cur_event.project.slug,
54+
'organization_slug': cur_event.project.organization.slug,
55+
}
56+
)
57+
response = self.client.get(url, format='json')
58+
59+
assert response.status_code == 200, response.content
60+
assert response.data['id'] == six.text_type(cur_event.id)
61+
assert response.data['nextEventID'] == six.text_type(next_event.event_id)
62+
assert response.data['previousEventID'] == six.text_type(prev_event.event_id)
63+
assert response.data['groupID'] == six.text_type(group.id)

0 commit comments

Comments
 (0)