Skip to content

Commit efd7381

Browse files
authored
feat: Add assigneeName to issue assigned activities (#85554)
Originally written by: @gabriellanata; It supersedes: #85399 > When an issue is assigned to a team, the activity only shows the team ID which is not very visual. I'm adding assigneeName (which matches the `assignedTo` when requesting an issue's details) to activity data. If you visit [this URL](https://us.sentry.io/api/0/organizations/sentry/issues/4704765109/activities/) you can see the current format of the activity (trimmed data): ``` { "activity": [ { "type": "assigned", "data": { "assignee": "2006237", "assigneeEmail": null, "assigneeType": "team" } } ] } ``` The `issues` team is not mentioned in the API, however, the UI handles it well: <img width="309" alt="image" src="https://github.com/user-attachments/assets/fbe76b20-ad50-40a8-b32c-35189a44ed39" /> Ideally, we would store less information in `data` and make the serializer smarter by fetching the models it is indirectly referring to (see [code](https://github.com/getsentry/sentry/blob/e96e32a095e1e2ff89c8fb0ea2681848decaf4c3/src/sentry/models/activity.py#L57-L66)).
1 parent 9cfea0e commit efd7381

File tree

8 files changed

+50
-5
lines changed

8 files changed

+50
-5
lines changed

src/sentry/models/groupassignee.py

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def get_assigned_to_data(
4141
data = {
4242
"assignee": str(assigned_to.id),
4343
"assigneeEmail": getattr(assigned_to, "email", None),
44+
"assigneeName": getattr(assigned_to, "name", None),
4445
"assigneeType": assignee_type,
4546
}
4647
if extra:

src/sentry/web/frontend/debug/debug_assigned_email.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def get_activity(self, request: AuthenticatedHttpRequest, event):
1212
"data": {
1313
"assignee": "10000000",
1414
"assigneeEmail": "[email protected]",
15+
"assigneeName": "Example User",
1516
"assigneeType": "user",
1617
},
1718
}
@@ -25,6 +26,7 @@ def get_activity(self, request: AuthenticatedHttpRequest, event):
2526
"data": {
2627
"assignee": str(request.user.id),
2728
"assigneeEmail": request.user.email,
29+
"assigneeName": request.user.name,
2830
"assigneeType": "user",
2931
},
3032
}
@@ -35,5 +37,10 @@ def get_activity(self, request: AuthenticatedHttpRequest, event):
3537
return {
3638
"type": ActivityType.ASSIGNED.value,
3739
"user_id": request.user.id,
38-
"data": {"assignee": "1", "assigneeEmail": None, "assigneeType": "team"},
40+
"data": {
41+
"assignee": "1",
42+
"assigneeEmail": None,
43+
"assigneeName": "example-team",
44+
"assigneeType": "team",
45+
},
3946
}

tests/sentry/integrations/msteams/test_action_state_change.py

+2
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ def test_assign_to_team(self, verify):
223223
assert activity.data == {
224224
"assignee": str(self.team.id),
225225
"assigneeEmail": None,
226+
"assigneeName": self.team.name,
226227
"assigneeType": "team",
227228
"integration": ActivityIntegration.MSTEAMS.value,
228229
}
@@ -242,6 +243,7 @@ def test_assign_to_me(self, verify, mock_record):
242243
assert activity.data == {
243244
"assignee": str(self.user.id),
244245
"assigneeEmail": self.user.email,
246+
"assigneeName": self.user.name,
245247
"assigneeType": "user",
246248
"integration": ActivityIntegration.MSTEAMS.value,
247249
}

tests/sentry/integrations/slack/webhooks/actions/test_status.py

+5
Original file line numberDiff line numberDiff line change
@@ -506,12 +506,14 @@ def test_assign_issue(self, mock_tags):
506506
assert group_activity[0].data == {
507507
"assignee": str(user2.id),
508508
"assigneeEmail": user2.email,
509+
"assigneeName": user2.name,
509510
"assigneeType": "user",
510511
"integration": ActivityIntegration.SLACK.value,
511512
}
512513
assert group_activity[-1].data == {
513514
"assignee": str(self.team.id),
514515
"assigneeEmail": None,
516+
"assigneeName": self.team.name,
515517
"assigneeType": "team",
516518
"integration": ActivityIntegration.SLACK.value,
517519
}
@@ -547,6 +549,7 @@ def test_assign_issue_error(self, mock_logger):
547549
assert group_activity[0].data == {
548550
"assignee": str(user2.id),
549551
"assigneeEmail": user2.email,
552+
"assigneeName": user2.name,
550553
"assigneeType": "user",
551554
"integration": ActivityIntegration.SLACK.value,
552555
}
@@ -584,12 +587,14 @@ def test_assign_issue_through_unfurl(self):
584587
assert group_activity[0].data == {
585588
"assignee": str(user2.id),
586589
"assigneeEmail": user2.email,
590+
"assigneeName": user2.name,
587591
"assigneeType": "user",
588592
"integration": ActivityIntegration.SLACK.value,
589593
}
590594
assert group_activity[-1].data == {
591595
"assignee": str(self.team.id),
592596
"assigneeEmail": None,
597+
"assigneeName": self.team.name,
593598
"assigneeType": "team",
594599
"integration": ActivityIntegration.SLACK.value,
595600
}

tests/sentry/models/test_groupassignee.py

+8
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def test_assign_user(self):
4444

4545
assert activity.data["assignee"] == str(self.user.id)
4646
assert activity.data["assigneeEmail"] == self.user.email
47+
assert activity.data["assigneeName"] == self.user.name
4748
assert activity.data["assigneeType"] == "user"
4849

4950
def test_assign_team(self):
@@ -59,6 +60,7 @@ def test_assign_team(self):
5960

6061
assert activity.data["assignee"] == str(self.team.id)
6162
assert activity.data["assigneeEmail"] is None
63+
assert activity.data["assigneeName"] == self.team.name
6264
assert activity.data["assigneeType"] == "team"
6365

6466
def test_create_only(self):
@@ -73,6 +75,7 @@ def test_create_only(self):
7375
)
7476
assert activity.data["assignee"] == str(self.user.id)
7577
assert activity.data["assigneeEmail"] == self.user.email
78+
assert activity.data["assigneeName"] == self.user.name
7679
assert activity.data["assigneeType"] == "user"
7780

7881
other_user = self.create_user()
@@ -88,6 +91,7 @@ def test_create_only(self):
8891
)
8992
assert activity.data["assignee"] == str(self.user.id)
9093
assert activity.data["assigneeEmail"] == self.user.email
94+
assert activity.data["assigneeName"] == self.user.name
9195
assert activity.data["assigneeType"] == "user"
9296

9397
def test_reassign_user_to_team(self):
@@ -111,10 +115,12 @@ def test_reassign_user_to_team(self):
111115

112116
assert activity[0].data["assignee"] == str(self.user.id)
113117
assert activity[0].data["assigneeEmail"] == self.user.email
118+
assert activity[0].data["assigneeName"] == self.user.name
114119
assert activity[0].data["assigneeType"] == "user"
115120

116121
assert activity[1].data["assignee"] == str(self.team.id)
117122
assert activity[1].data["assigneeEmail"] is None
123+
assert activity[1].data["assigneeName"] == self.team.name
118124
assert activity[1].data["assigneeType"] == "team"
119125

120126
@mock.patch.object(ExampleIntegration, "sync_assignee_outbound")
@@ -174,6 +180,7 @@ def test_assignee_sync_outbound_assign(self, mock_sync_assignee_outbound):
174180

175181
assert activity.data["assignee"] == str(self.user.id)
176182
assert activity.data["assigneeEmail"] == self.user.email
183+
assert activity.data["assigneeName"] == self.user.name
177184
assert activity.data["assigneeType"] == "user"
178185

179186
@mock.patch.object(ExampleIntegration, "sync_assignee_outbound")
@@ -233,6 +240,7 @@ def test_assignee_sync_outbound_assign_with_matching_source_integration(
233240

234241
assert activity.data["assignee"] == str(self.user.id)
235242
assert activity.data["assigneeEmail"] == self.user.email
243+
assert activity.data["assigneeName"] == self.user.name
236244
assert activity.data["assigneeType"] == "user"
237245

238246
@mock.patch.object(ExampleIntegration, "sync_assignee_outbound")

tests/sentry/notifications/notifications/test_assigned.py

+24-4
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ def test_sends_reassignment_notification_user(self, mock_post):
122122
data={"assignedTo": user1.username, "assignedBy": user1.username},
123123
)
124124
assert response.status_code == 200, response.content
125-
data = {"assignee": str(user1.id), "assigneeEmail": user1.email, "assigneeType": "user"}
125+
data = {
126+
"assignee": str(user1.id),
127+
"assigneeEmail": user1.email,
128+
"assigneeName": user1.name,
129+
"assigneeType": "user",
130+
}
126131
assert Activity.objects.filter(
127132
group_id=self.group.id, type=ActivityType.ASSIGNED.value, user_id=user1.id, data=data
128133
).exists()
@@ -143,7 +148,12 @@ def test_sends_reassignment_notification_user(self, mock_post):
143148
data={"assignedTo": user2.username, "assignedBy": user1.username},
144149
)
145150
assert response.status_code == 200, response.content
146-
data = {"assignee": str(user2.id), "assigneeEmail": user2.email, "assigneeType": "user"}
151+
data = {
152+
"assignee": str(user2.id),
153+
"assigneeEmail": user2.email,
154+
"assigneeName": user2.name,
155+
"assigneeType": "user",
156+
}
147157
assert Activity.objects.filter(
148158
group_id=self.group.id, type=ActivityType.ASSIGNED.value, user_id=user1.id, data=data
149159
).exists()
@@ -190,7 +200,12 @@ def test_sends_reassignment_notification_team(self, mock_post):
190200
data={"assignedTo": f"team:{team1.id}", "assignedBy": self.user.username},
191201
)
192202
assert response.status_code == 200, response.content
193-
data = {"assignee": str(team1.id), "assigneeEmail": None, "assigneeType": "team"}
203+
data = {
204+
"assignee": str(team1.id),
205+
"assigneeEmail": None,
206+
"assigneeName": team1.name,
207+
"assigneeType": "team",
208+
}
194209
assert Activity.objects.filter(
195210
group_id=group.id, user_id=user1.id, type=ActivityType.ASSIGNED.value, data=data
196211
).exists()
@@ -213,7 +228,12 @@ def test_sends_reassignment_notification_team(self, mock_post):
213228
data={"assignedTo": f"team:{team2.id}", "assignedBy": self.user.username},
214229
)
215230
assert response.status_code == 200, response.content
216-
data = {"assignee": str(team2.id), "assigneeEmail": None, "assigneeType": "team"}
231+
data = {
232+
"assignee": str(team2.id),
233+
"assigneeEmail": None,
234+
"assigneeName": team2.name,
235+
"assigneeType": "team",
236+
}
217237
assert Activity.objects.filter(
218238
group_id=group.id, user_id=user1.id, type=ActivityType.ASSIGNED.value, data=data
219239
).exists()

tests/sentry/receivers/test_releases.py

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ def test_matching_author_with_assignment(self):
190190
)[0].data == {
191191
"assignee": str(user.id),
192192
"assigneeEmail": user.email,
193+
"assigneeName": user.name,
193194
"assigneeType": "user",
194195
}
195196

tests/sentry/tasks/test_post_process.py

+1
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@ def test_owner_assignment_order_precedence(self):
823823
assert activity.data == {
824824
"assignee": str(self.user.id),
825825
"assigneeEmail": self.user.email,
826+
"assigneeName": self.user.name,
826827
"assigneeType": "user",
827828
"integration": ActivityIntegration.PROJECT_OWNERSHIP.value,
828829
"rule": str(Rule(Matcher("path", "src/*"), [Owner("user", self.user.email)])),

0 commit comments

Comments
 (0)