Skip to content

feat: Allow id or event_id in project events endpoint #10898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions src/sentry/api/endpoints/project_event_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from sentry.api.base import DocSection
from sentry.api.bases.project import ProjectEndpoint
from sentry.api.serializers import serialize
from sentry.models import Event
from sentry.models import Event, Release, UserReport
from sentry.utils.apidocs import scenario, attach_scenarios
from sentry.utils.validators import is_event_id


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

def _get_release_info(self, request, event):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

version = event.get_tag('sentry:release')
if not version:
return None
try:
release = Release.objects.get(
projects=event.project,
organization_id=event.project.organization_id,
version=version,
)
except Release.DoesNotExist:
return {'version': version}
return serialize(release, request.user)

@attach_scenarios([retrieve_event_for_project_scenario])
def get(self, request, project, event_id):
"""
Expand All @@ -35,16 +50,33 @@ def get(self, request, project, event_id):
event belongs to.
:pparam string project_slug: the slug of the project the event
belongs to.
:pparam string event_id: the hexadecimal ID of the event to
retrieve (as reported by the raven client).
:pparam string event_id: the id of the event to retrieve (either the
numeric primary-key or the hexadecimal id as
reported by the raven client)
:auth: required
"""
try:
event = Event.objects.get(
event_id=event_id,
project_id=project.id,
)
except Event.DoesNotExist:

event = None
# If its a numeric string, check if it's an event Primary Key first
if event_id.isdigit():
try:
event = Event.objects.get(
id=event_id,
project_id=project.id,
)
except Event.DoesNotExist:
pass
# If it was not found as a PK, and its a possible event_id, search by that instead.
if event is None and is_event_id(event_id):
try:
event = Event.objects.get(
event_id=event_id,
project_id=project.id,
)
except Event.DoesNotExist:
pass

if event is None:
return Response({'detail': 'Event not found'}, status=404)

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

try:
user_report = UserReport.objects.get(
event_id=event.event_id,
project=event.project,
)
except UserReport.DoesNotExist:
user_report = None

data = serialize(event, request.user)
data['userReport'] = serialize(user_report, request.user)
data['release'] = self._get_release_info(request, event)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This userReport and release stuff is just to make the payload returned by this endpoint the same as the /events/ one

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would probably be nice to move the shared stuff between this and the other endpoint into a serializer or something (maybe DetailedEventSerializer?) but doesn't need to be part of this pr

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Have done it here. #10921


if next_event:
data['nextEventID'] = six.text_type(next_event.event_id)
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@
name='sentry-api-0-project-events'
),
url(
r'^projects/(?P<organization_slug>[^\/]+)/(?P<project_slug>[^\/]+)/events/(?P<event_id>[\w-]+)/$',
r'^projects/(?P<organization_slug>[^\/]+)/(?P<project_slug>[^\/]+)/events/(?P<event_id>(?:\d+|[A-Fa-f0-9]{32}))/$',
ProjectEventDetailsEndpoint.as_view(),
name='sentry-api-0-project-event-details'
),
Expand Down
15 changes: 9 additions & 6 deletions src/sentry/static/sentry/app/views/groupEventDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ const GroupEventDetails = createReactClass({
},

fetchData() {
let eventId = this.props.params.eventId || 'latest';
const eventId = this.props.params.eventId || 'latest';
const groupId = this.getGroup().id;
const orgSlug = this.getOrganization().slug;
const projSlug = this.getProject().slug;

let url =
eventId === 'latest' || eventId === 'oldest'
? '/issues/' + this.getGroup().id + '/events/' + eventId + '/'
: '/events/' + eventId + '/';
? `/issues/${groupId}/events/${eventId}/`
: `/projects/${orgSlug}/${projSlug}/events/${eventId}/`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure this is the only place that called events/eventId but that endpoint could potentially be deprecated if it is.


this.setState({
loading: true,
Expand All @@ -59,9 +62,9 @@ const GroupEventDetails = createReactClass({
});

this.api.bulkUpdate({
orgId: this.getOrganization().slug,
projectId: this.getProject().slug,
itemIds: [this.getGroup().id],
orgId: orgSlug,
projectId: projSlug,
itemIds: [groupId],
failSilently: true,
data: {hasSeen: true},
});
Expand Down
23 changes: 20 additions & 3 deletions tests/sentry/api/endpoints/test_project_event_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ def test_simple(self):

group = self.create_group()
prev_event = self.create_event(
event_id='a',
event_id='a' * 32,
group=group,
datetime=datetime(2013, 8, 13, 3, 8, 24),
)
cur_event = self.create_event(
event_id='b',
event_id='b' * 32,
group=group,
datetime=datetime(2013, 8, 13, 3, 8, 25),
)
next_event = self.create_event(
event_id='c',
event_id='c' * 32,
group=group,
datetime=datetime(2013, 8, 13, 3, 8, 26),
)
Expand All @@ -44,3 +44,20 @@ def test_simple(self):
assert response.data['nextEventID'] == six.text_type(next_event.event_id)
assert response.data['previousEventID'] == six.text_type(prev_event.event_id)
assert response.data['groupID'] == six.text_type(group.id)

# Same event can be looked up by primary key
url = reverse(
'sentry-api-0-project-event-details',
kwargs={
'event_id': cur_event.id,
'project_slug': cur_event.project.slug,
'organization_slug': cur_event.project.organization.slug,
}
)
response = self.client.get(url, format='json')

assert response.status_code == 200, response.content
assert response.data['id'] == six.text_type(cur_event.id)
assert response.data['nextEventID'] == six.text_type(next_event.event_id)
assert response.data['previousEventID'] == six.text_type(prev_event.event_id)
assert response.data['groupID'] == six.text_type(group.id)