Skip to content

Commit b484542

Browse files
authored
feat(eventsv2) Add basic transaction list (#14103)
Build basic transaction subtab in events v2 list. Add `transactionSlug` as a parameter to open the event details modal. Because we don't have a synthetic grouping column (like issue.id) for transaction events we need to rely on the combination of `project_id` and `transaction` name to act as a key to group transactions together. With these two pieces of data we can generate the summary graphs and locate the 'latest' event in the transaction series. Use aria-label instead of data-test-id. The label lets us select a specific row more easily and confidently. Refs SEN-796
1 parent 37380c7 commit b484542

File tree

5 files changed

+53
-12
lines changed

5 files changed

+53
-12
lines changed

src/sentry/api/endpoints/organization_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from sentry import features
1919
from sentry.models.project import Project
2020

21-
ALLOWED_GROUPINGS = frozenset(('issue.id', 'project.id'))
21+
ALLOWED_GROUPINGS = frozenset(('issue.id', 'project.id', 'transaction'))
2222
logger = logging.getLogger(__name__)
2323

2424

src/sentry/static/sentry/app/views/organizationEventsV2/data.jsx

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import UserBadge from 'app/components/idBadge/userBadge';
1212
import DateTime from 'app/components/dateTime';
1313
import pinIcon from 'app/../images/location-pin.png';
1414

15+
import {t} from 'app/locale';
1516
import {QueryLink} from './styles';
1617

1718
export const MODAL_QUERY_KEYS = ['eventSlug', 'groupSlug'];
@@ -20,7 +21,7 @@ export const PIN_ICON = `image://${pinIcon}`;
2021
export const ALL_VIEWS = deepFreeze([
2122
{
2223
id: 'all',
23-
name: 'All Events',
24+
name: t('All Events'),
2425
data: {
2526
fields: ['event', 'type', 'project', 'user', 'time'],
2627
sort: ['-timestamp', '-id'],
@@ -37,7 +38,7 @@ export const ALL_VIEWS = deepFreeze([
3738
},
3839
{
3940
id: 'errors',
40-
name: 'Errors',
41+
name: t('Errors'),
4142
data: {
4243
fields: ['error', 'event_count', 'user_count', 'project', 'last_seen'],
4344
groupby: ['issue.id', 'project.id'],
@@ -49,7 +50,7 @@ export const ALL_VIEWS = deepFreeze([
4950
},
5051
{
5152
id: 'csp',
52-
name: 'CSP',
53+
name: t('CSP'),
5354
data: {
5455
fields: ['csp', 'event_count', 'user_count', 'project', 'last_seen'],
5556
groupby: ['issue.id', 'project.id'],
@@ -65,6 +66,25 @@ export const ALL_VIEWS = deepFreeze([
6566
],
6667
columnWidths: ['3fr', '70px', '70px', '1fr', '1.5fr'],
6768
},
69+
{
70+
id: 'transactions',
71+
name: t('Transactions'),
72+
data: {
73+
fields: ['transaction', 'project'],
74+
groupby: ['transaction', 'project.id'],
75+
sort: ['-transaction'],
76+
query: 'event.type:transaction',
77+
},
78+
tags: [
79+
'event.type',
80+
'release',
81+
'project.name',
82+
'user.email',
83+
'user.ip',
84+
'environment',
85+
],
86+
columnWidths: ['3fr', '1fr', '1fr', '1fr', '1fr', '1fr', '1fr'],
87+
},
6888
]);
6989

7090
/**
@@ -73,6 +93,26 @@ export const ALL_VIEWS = deepFreeze([
7393
* displays with a custom render function.
7494
*/
7595
export const SPECIAL_FIELDS = {
96+
transaction: {
97+
fields: ['project.name', 'transaction'],
98+
sortField: 'transaction',
99+
renderFunc: (data, {organization, location}) => {
100+
const target = {
101+
pathname: `/organizations/${organization.slug}/events/`,
102+
query: {
103+
...location.query,
104+
transactionSlug: `${data['project.name']}:${data.transaction}`,
105+
},
106+
};
107+
return (
108+
<Container>
109+
<Link css={overflowEllipsis} to={target} aria-label={data.transaction}>
110+
{data.transaction}
111+
</Link>
112+
</Container>
113+
);
114+
},
115+
},
76116
event: {
77117
fields: ['title', 'id', 'project.name'],
78118
sortField: 'title',
@@ -83,7 +123,7 @@ export const SPECIAL_FIELDS = {
83123
};
84124
return (
85125
<Container>
86-
<Link css={overflowEllipsis} to={target} data-test-id="event-title">
126+
<Link css={overflowEllipsis} to={target} aria-label={data.title}>
87127
{data.title}
88128
</Link>
89129
</Container>
@@ -176,7 +216,7 @@ export const SPECIAL_FIELDS = {
176216
};
177217
return (
178218
<Container>
179-
<Link css={overflowEllipsis} to={target} data-test-id="event-title">
219+
<Link css={overflowEllipsis} to={target} aria-label={data.issue_title}>
180220
{data.issue_title}
181221
</Link>
182222
</Container>
@@ -196,7 +236,7 @@ export const SPECIAL_FIELDS = {
196236
};
197237
return (
198238
<Container>
199-
<Link css={overflowEllipsis} to={target} data-test-id="event-title">
239+
<Link css={overflowEllipsis} to={target} aria-label={data.issue_title}>
200240
{data.issue_title}
201241
</Link>
202242
</Container>

tests/acceptance/test_organization_events_v2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def test_modal_from_all_events(self, mock_now):
127127
self.wait_until_loaded()
128128

129129
# Click the event link to open the modal
130-
self.browser.element('[data-test-id="event-title"]').click()
130+
self.browser.element('[aria-label="{}"]'.format(event.title)).click()
131131
self.wait_until_loaded()
132132

133133
header = self.browser.element('[data-test-id="modal-dialog"] h2')
@@ -169,7 +169,7 @@ def test_modal_from_errors_view(self, mock_now):
169169
self.wait_until_loaded()
170170

171171
# Click the event link to open the modal
172-
self.browser.element('[data-test-id="event-title"]').click()
172+
self.browser.element('[aria-label="{}"]'.format(event.title)).click()
173173
self.wait_until_loaded()
174174

175175
self.browser.snapshot('events-v2 - grouped error modal')

tests/js/spec/views/organizationEventsV2/index.spec.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import {mount} from 'enzyme';
44
import OrganizationEventsV2 from 'app/views/organizationEventsV2';
55

66
describe('OrganizationEventsV2', function() {
7+
const eventTitle = 'Oh no something bad';
78
beforeEach(function() {
89
MockApiClient.addMockResponse({
910
url: '/organizations/org-slug/events/',
1011
body: [
1112
{
1213
id: 'deadbeef',
13-
title: 'Oh no something bad',
14+
title: eventTitle,
1415
'project.name': 'project-slug',
1516
timestamp: '2019-05-23T22:12:48+00:00',
1617
},
@@ -108,7 +109,7 @@ describe('OrganizationEventsV2', function() {
108109
TestStubs.routerContext()
109110
);
110111

111-
const link = wrapper.find('Table Link[data-test-id="event-title"]').first();
112+
const link = wrapper.find(`Table Link[aria-label="${eventTitle}"]`).first();
112113
expect(link.props().to.query).toEqual({eventSlug: 'project-slug:deadbeef'});
113114
});
114115

tests/snuba/api/endpoints/test_organization_events_v2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def test_invalid_groupby(self):
530530
},
531531
)
532532
assert response.status_code == 400, response.content
533-
assert response.data['detail'] == 'Invalid groupby value requested. Allowed values are project.id, issue.id'
533+
assert response.data['detail'] == 'Invalid groupby value requested. Allowed values are transaction, project.id, issue.id'
534534

535535
def test_non_aggregated_fields_with_groupby(self):
536536
self.login_as(user=self.user)

0 commit comments

Comments
 (0)