Skip to content

Commit 93b2f82

Browse files
committed
feat(crons): Add monitor slug to APIs
* Returns slug as part of the serialized monitor * Allows slug to be specified for creation and updates
1 parent 41e29c9 commit 93b2f82

File tree

7 files changed

+44
-1
lines changed

7 files changed

+44
-1
lines changed

src/sentry/api/endpoints/organization_monitor_details.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ def put(
9191
params = {}
9292
if "name" in result:
9393
params["name"] = result["name"]
94+
if "slug" in result:
95+
params["slug"] = result["slug"]
9496
if "status" in result:
9597
if result["status"] == MonitorStatus.ACTIVE:
9698
if monitor.status not in (MonitorStatus.OK, MonitorStatus.ERROR):

src/sentry/api/endpoints/organization_monitors.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def post(self, request: Request, organization) -> Response:
122122
project_id=result["project"].id,
123123
organization_id=organization.id,
124124
name=result["name"],
125+
slug=result.get("slug"),
125126
status=result["status"],
126127
type=result["type"],
127128
config=result["config"],

src/sentry/api/serializers/models/monitor.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def serialize(self, obj, attrs, user):
3434
"status": obj.get_status_display(),
3535
"type": obj.get_type_display(),
3636
"name": obj.name,
37+
"slug": obj.slug,
3738
"config": config,
3839
"lastCheckIn": obj.last_checkin,
3940
"nextCheckIn": obj.next_checkin,
@@ -45,6 +46,7 @@ def serialize(self, obj, attrs, user):
4546
class MonitorSerializerResponse(TypedDict):
4647
id: str
4748
name: str
49+
slug: str
4850
status: str
4951
type: str
5052
config: Any

src/sentry/api/validators/monitor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def validate(self, attrs):
8989
class MonitorValidator(serializers.Serializer):
9090
project = ProjectField(scope="project:read")
9191
name = serializers.CharField()
92+
slug = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50, required=False)
9293
status = serializers.ChoiceField(
9394
choices=list(zip(MONITOR_STATUSES.keys(), MONITOR_STATUSES.keys())), default="active"
9495
)

src/sentry/models/monitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def save(self, *args, **kwargs):
156156
# NOTE: We ONLY set a slug while saving when creating a new monitor and
157157
# the slug has not been set. Otherwise existing monitors without slugs
158158
# would have their guids changed
159-
if self._state.adding is True and self.slug == "":
159+
if self._state.adding is True and not self.slug:
160160
self.guid = uuid4()
161161
self.slug = str(self.guid)
162162
return super().save(*args, **kwargs)

tests/sentry/api/endpoints/test_organization_monitor_details.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ def test_simple_slug(self):
2222

2323
resp = self.get_success_response(self.organization.slug, monitor.slug)
2424
assert resp.data["id"] == str(monitor.guid)
25+
assert resp.data["slug"] == "my-monitor"
26+
27+
# GUID based lookup also works
28+
resp = self.get_success_response(self.organization.slug, monitor.guid)
29+
assert resp.data["id"] == str(monitor.guid)
30+
assert resp.data["slug"] == "my-monitor"
2531

2632
def test_mismatched_org_slugs(self):
2733
monitor = self._create_monitor()
@@ -50,6 +56,24 @@ def test_name(self):
5056
monitor = Monitor.objects.get(id=monitor.id)
5157
assert monitor.name == "Monitor Name"
5258

59+
def test_slug(self):
60+
monitor = self._create_monitor()
61+
resp = self.get_success_response(
62+
self.organization.slug, monitor.guid, method="PUT", **{"slug": "my-monitor"}
63+
)
64+
assert resp.data["id"] == str(monitor.guid)
65+
66+
monitor = Monitor.objects.get(id=monitor.id)
67+
assert monitor.slug == "my-monitor"
68+
69+
# Validate error cases jsut to be safe
70+
self.get_error_response(
71+
self.organization.slug, monitor.guid, method="PUT", status_code=400, **{"slug": ""}
72+
)
73+
self.get_error_response(
74+
self.organization.slug, monitor.guid, method="PUT", status_code=400, **{"slug": None}
75+
)
76+
5377
def test_can_disable(self):
5478
monitor = self._create_monitor()
5579
resp = self.get_success_response(

tests/sentry/api/endpoints/test_organization_monitors.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,16 @@ def test_simple(self, mock_record):
105105
organization_id=self.organization.id,
106106
project_id=self.project.id,
107107
)
108+
109+
def test_slug(self):
110+
data = {
111+
"project": self.project.slug,
112+
"name": "My Monitor",
113+
"slug": "my-monitor",
114+
"type": "cron_job",
115+
"config": {"schedule_type": "crontab", "schedule": "@daily"},
116+
}
117+
response = self.get_success_response(self.organization.slug, **data)
118+
119+
assert response.data["id"]
120+
assert response.data["slug"] == "my-monitor"

0 commit comments

Comments
 (0)