Skip to content

Commit 9c50d52

Browse files
committed
fix(crons): allow team and user names to be used in checkin payload
1 parent 77f9b12 commit 9c50d52

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/sentry/types/actor.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,18 +166,23 @@ def from_identifier(cls, id: None) -> None: ...
166166
def from_identifier(cls, id: int | str) -> "Actor": ...
167167

168168
@classmethod
169-
def from_identifier(cls, id: str | int | None) -> "Actor | None":
169+
def from_identifier(
170+
cls, id: str | int | None, organization_id: int | None = None
171+
) -> "Actor | None":
170172
"""
171173
Parse an actor identifier into an Actor
172174
173175
Forms `id` can take:
174176
1231 -> look up User by id
175177
"1231" -> look up User by id
176178
"user:1231" -> look up User by id
179+
"user:maiseythedog" -> look up user by username
177180
"team:1231" -> look up Team by id
181+
"team:team-name" -> look up Team by name (must provide organization_id)
178182
"maiseythedog" -> look up User by username
179183
"[email protected]" -> look up User by primary email
180184
"""
185+
from sentry.models.team import Team
181186
from sentry.users.services.user.service import user_service
182187

183188
if not id:
@@ -192,10 +197,23 @@ def from_identifier(cls, id: str | int | None) -> "Actor | None":
192197
return cls(id=int(id), actor_type=ActorType.USER)
193198

194199
if id.startswith("user:"):
195-
return cls(id=int(id[5:]), actor_type=ActorType.USER)
200+
remainder = id[5:]
201+
if remainder.isdigit():
202+
return cls(id=int(remainder), actor_type=ActorType.USER)
203+
# pass this on to get to the user lookup below
204+
id = remainder
196205

197206
if id.startswith("team:"):
198-
return cls(id=int(id[5:]), actor_type=ActorType.TEAM)
207+
remainder = id[5:]
208+
if remainder.isnumeric():
209+
return cls(id=int(remainder), actor_type=ActorType.TEAM)
210+
211+
if organization_id is not None:
212+
team = Team.objects.filter(name=remainder, organization_id=organization_id)
213+
if team.exists():
214+
return cls(id=team.first().id, actor_type=ActorType.TEAM)
215+
216+
raise cls.InvalidActor(f"Unable to resolve team name: {remainder}")
199217

200218
try:
201219
user = user_service.get_by_username(username=id)[0]
@@ -280,7 +298,7 @@ def parse_and_validate_actor(actor_identifier: str | None, organization_id: int)
280298
return None
281299

282300
try:
283-
actor = Actor.from_identifier(actor_identifier)
301+
actor = Actor.from_identifier(actor_identifier, organization_id)
284302
except Exception:
285303
raise serializers.ValidationError(
286304
"Could not parse actor. Format should be `type:id` where type is `team` or `user`."

tests/sentry/monitors/consumers/test_monitor_consumer.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,49 @@ def test_monitor_upsert_empty_timezone(self):
940940
assert monitor is not None
941941
assert "timezone" not in monitor.config
942942

943+
def test_team_name_as_owner(self):
944+
monitor = self._create_monitor(slug="my-monitor", owner_user_id=self.user.id)
945+
self.send_checkin(
946+
"my-monitor",
947+
monitor_config={
948+
"schedule": {"type": "crontab", "value": "13 * * * *"},
949+
"owner": f"team:{self.team.name}",
950+
},
951+
)
952+
checkin = MonitorCheckIn.objects.get(guid=self.guid)
953+
assert checkin.status == CheckInStatus.OK
954+
955+
monitor_environment = MonitorEnvironment.objects.get(id=checkin.monitor_environment.id)
956+
assert monitor_environment.status == MonitorStatus.OK
957+
monitor.refresh_from_db()
958+
assert monitor.owner_user_id is None
959+
assert monitor.owner_team_id == self.team.id
960+
961+
def test_user_name_as_owner(self):
962+
named_user = self.create_user(
963+
"admin2@localhost",
964+
username="test_user",
965+
is_superuser=True,
966+
is_staff=True,
967+
is_sentry_app=False,
968+
)
969+
monitor = self._create_monitor(slug="my-monitor", owner_user_id=named_user.id)
970+
971+
self.send_checkin(
972+
"my-monitor",
973+
monitor_config={
974+
"schedule": {"type": "crontab", "value": "13 * * * *"},
975+
"owner": f"user:{named_user.username}",
976+
},
977+
)
978+
checkin = MonitorCheckIn.objects.get(guid=self.guid)
979+
assert checkin.status == CheckInStatus.OK
980+
981+
monitor_environment = MonitorEnvironment.objects.get(id=checkin.monitor_environment.id)
982+
assert monitor_environment.status == MonitorStatus.OK
983+
assert monitor.owner_user_id == named_user.id
984+
assert monitor.owner_team_id is None
985+
943986
def test_monitor_upsert_invalid_slug(self):
944987
self.send_checkin(
945988
"some/slug@with-weird|stuff",

0 commit comments

Comments
 (0)