Skip to content

Commit 64eae03

Browse files
authored
feat(api-idorslug): Update Subset of Monitor & Integration Endpoints and to use organization_id_or_slug (#70784)
A subset of changes from #70081!
1 parent 48d9c5c commit 64eae03

24 files changed

+98
-80
lines changed

src/sentry/api/endpoints/organization_unsubscribe.py

+21-13
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class OrganizationUnsubscribeBase(Endpoint, Generic[T]):
3737

3838
object_type = "unknown"
3939

40-
def fetch_instance(self, request: Request, organization_slug: str, id: int) -> T:
40+
def fetch_instance(self, request: Request, organization_id_or_slug: int | str, id: int) -> T:
4141
raise NotImplementedError()
4242

4343
def unsubscribe(self, request: Request, instance: T):
@@ -46,10 +46,12 @@ def unsubscribe(self, request: Request, instance: T):
4646
def add_instance_data(self, data: dict[str, Any], instance: T) -> dict[str, Any]:
4747
return data
4848

49-
def get(self, request: Request, organization_slug: str, id: int, **kwargs) -> Response:
49+
def get(
50+
self, request: Request, organization_id_or_slug: int | str, id: int, **kwargs
51+
) -> Response:
5052
if not request.user_from_signed_request:
5153
raise NotFound()
52-
instance = self.fetch_instance(request, organization_slug, id)
54+
instance = self.fetch_instance(request, organization_id_or_slug, id)
5355
view_url = ""
5456
if hasattr(instance, "get_absolute_url"):
5557
view_url = str(instance.get_absolute_url())
@@ -65,10 +67,12 @@ def get(self, request: Request, organization_slug: str, id: int, **kwargs) -> Re
6567
}
6668
return Response(self.add_instance_data(data, instance), 200)
6769

68-
def post(self, request: Request, organization_slug: str, id: int, **kwargs) -> Response:
70+
def post(
71+
self, request: Request, organization_id_or_slug: int | str, id: int, **kwargs
72+
) -> Response:
6973
if not request.user_from_signed_request:
7074
raise NotFound()
71-
instance = self.fetch_instance(request, organization_slug, id)
75+
instance = self.fetch_instance(request, organization_id_or_slug, id)
7276

7377
if request.data.get("cancel"):
7478
self.unsubscribe(request, instance)
@@ -79,16 +83,18 @@ def post(self, request: Request, organization_slug: str, id: int, **kwargs) -> R
7983
class OrganizationUnsubscribeProject(OrganizationUnsubscribeBase[Project]):
8084
object_type = "project"
8185

82-
def fetch_instance(self, request: Request, organization_slug: str, id: int) -> Project:
86+
def fetch_instance(
87+
self, request: Request, organization_id_or_slug: int | str, id: int
88+
) -> Project:
8389
try:
8490
project = Project.objects.select_related("organization").get(id=id)
8591
except Project.DoesNotExist:
8692
raise NotFound()
87-
if str(organization_slug).isdecimal():
88-
if project.organization.id != int(organization_slug):
93+
if str(organization_id_or_slug).isdecimal():
94+
if project.organization.id != int(organization_id_or_slug):
8995
raise NotFound()
9096
else:
91-
if project.organization.slug != organization_slug:
97+
if project.organization.slug != organization_id_or_slug:
9298
raise NotFound()
9399
if not OrganizationMember.objects.filter(
94100
user_id=request.user.pk, organization_id=project.organization_id
@@ -115,16 +121,18 @@ def unsubscribe(self, request: Request, instance: Project):
115121
class OrganizationUnsubscribeIssue(OrganizationUnsubscribeBase[Group]):
116122
object_type = "issue"
117123

118-
def fetch_instance(self, request: Request, organization_slug: str, issue_id: int) -> Group:
124+
def fetch_instance(
125+
self, request: Request, organization_id_or_slug: int | str, issue_id: int
126+
) -> Group:
119127
try:
120128
issue = Group.objects.get_from_cache(id=issue_id)
121129
except Group.DoesNotExist:
122130
raise NotFound()
123-
if str(organization_slug).isdecimal():
124-
if issue.organization.id != int(organization_slug):
131+
if str(organization_id_or_slug).isdecimal():
132+
if issue.organization.id != int(organization_id_or_slug):
125133
raise NotFound()
126134
else:
127-
if issue.organization.slug != organization_slug:
135+
if issue.organization.slug != organization_id_or_slug:
128136
raise NotFound()
129137

130138
if not OrganizationMember.objects.filter(

src/sentry/api/urls.py

+17-17
Original file line numberDiff line numberDiff line change
@@ -1643,47 +1643,47 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
16431643
),
16441644
# Monitors
16451645
re_path(
1646-
r"^(?P<organization_slug>[^\/]+)/monitors/$",
1646+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors/$",
16471647
OrganizationMonitorIndexEndpoint.as_view(),
16481648
name="sentry-api-0-organization-monitor-index",
16491649
),
16501650
re_path(
1651-
r"^(?P<organization_slug>[^\/]+)/monitors-stats/$",
1651+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors-stats/$",
16521652
OrganizationMonitorIndexStatsEndpoint.as_view(),
16531653
name="sentry-api-0-organization-monitor-index-stats",
16541654
),
16551655
re_path(
1656-
r"^(?P<organization_slug>[^\/]+)/processing-errors/$",
1656+
r"^(?P<organization_id_or_slug>[^\/]+)/processing-errors/$",
16571657
OrganizationMonitorProcessingErrorsIndexEndpoint.as_view(),
16581658
name="sentry-api-0-organization-monitor-processing-errors-index",
16591659
),
16601660
re_path(
1661-
r"^(?P<organization_slug>[^\/]+)/monitors-schedule-data/$",
1661+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors-schedule-data/$",
16621662
OrganizationMonitorScheduleSampleDataEndpoint.as_view(),
16631663
name="sentry-api-0-organization-monitors-schedule-sample-data",
16641664
),
16651665
re_path(
1666-
r"^(?P<organization_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/$",
1666+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/$",
16671667
OrganizationMonitorDetailsEndpoint.as_view(),
16681668
name="sentry-api-0-organization-monitor-details",
16691669
),
16701670
re_path(
1671-
r"^(?P<organization_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/environments/(?P<environment>[^\/]+)$",
1671+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/environments/(?P<environment>[^\/]+)$",
16721672
OrganizationMonitorEnvironmentDetailsEndpoint.as_view(),
16731673
name="sentry-api-0-organization-monitor-environment-details",
16741674
),
16751675
re_path(
1676-
r"^(?P<organization_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/stats/$",
1676+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/stats/$",
16771677
OrganizationMonitorStatsEndpoint.as_view(),
16781678
name="sentry-api-0-organization-monitor-stats",
16791679
),
16801680
re_path(
1681-
r"^(?P<organization_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/$",
1681+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/$",
16821682
OrganizationMonitorCheckInIndexEndpoint.as_view(),
16831683
name="sentry-api-0-organization-monitor-check-in-index",
16841684
),
16851685
re_path(
1686-
r"^(?P<organization_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/(?P<checkin_id>[^\/]+)/attachment/$",
1686+
r"^(?P<organization_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/(?P<checkin_id>[^\/]+)/attachment/$",
16871687
method_dispatch(
16881688
GET=OrganizationMonitorCheckInAttachmentEndpoint.as_view(),
16891689
OPTIONS=OrganizationMonitorCheckInAttachmentEndpoint.as_view(),
@@ -2120,12 +2120,12 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
21202120
),
21212121
# Unsubscribe from organization notifications
21222122
re_path(
2123-
r"^(?P<organization_slug>[^/]+)/unsubscribe/project/(?P<id>\d+)/$",
2123+
r"^(?P<organization_id_or_slug>[^/]+)/unsubscribe/project/(?P<id>\d+)/$",
21242124
OrganizationUnsubscribeProject.as_view(),
21252125
name="sentry-api-0-organization-unsubscribe-project",
21262126
),
21272127
re_path(
2128-
r"^(?P<organization_slug>[^/]+)/unsubscribe/issue/(?P<id>\d+)/$",
2128+
r"^(?P<organization_id_or_slug>[^/]+)/unsubscribe/issue/(?P<id>\d+)/$",
21292129
OrganizationUnsubscribeIssue.as_view(),
21302130
name="sentry-api-0-organization-unsubscribe-issue",
21312131
),
@@ -2743,22 +2743,22 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
27432743
name="sentry-api-0-project-statistical-detector",
27442744
),
27452745
re_path(
2746-
r"^(?P<organization_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/(?P<checkin_id>[^\/]+)/attachment/$",
2746+
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/(?P<checkin_id>[^\/]+)/attachment/$",
27472747
ProjectMonitorCheckInAttachmentEndpoint.as_view(),
27482748
name="sentry-api-0-project-monitor-check-in-attachment",
27492749
),
27502750
re_path(
2751-
r"^(?P<organization_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/$",
2751+
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/$",
27522752
ProjectMonitorDetailsEndpoint.as_view(),
27532753
name="sentry-api-0-project-monitor-details",
27542754
),
27552755
re_path(
2756-
r"^(?P<organization_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/$",
2756+
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/checkins/$",
27572757
ProjectMonitorCheckInIndexEndpoint.as_view(),
27582758
name="sentry-api-0-project-monitor-check-in-index",
27592759
),
27602760
re_path(
2761-
r"^(?P<organization_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/environments/(?P<environment>[^\/]+)$",
2761+
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/environments/(?P<environment>[^\/]+)$",
27622762
ProjectMonitorEnvironmentDetailsEndpoint.as_view(),
27632763
name="sentry-api-0-project-monitor-environment-details",
27642764
),
@@ -2768,12 +2768,12 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]:
27682768
name="sentry-api-0-project-processing-errors-details",
27692769
),
27702770
re_path(
2771-
r"^(?P<organization_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/processing-errors/$",
2771+
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/processing-errors/$",
27722772
ProjectMonitorProcessingErrorsIndexEndpoint.as_view(),
27732773
name="sentry-api-0-project-monitor-processing-errors-index",
27742774
),
27752775
re_path(
2776-
r"^(?P<organization_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/stats/$",
2776+
r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/monitors/(?P<monitor_id_or_slug>[^\/]+)/stats/$",
27772777
ProjectMonitorStatsEndpoint.as_view(),
27782778
name="sentry-api-0-project-monitor-stats",
27792779
),

src/sentry/integrations/bitbucket/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
name="sentry-extensions-bitbucket-webhook",
2929
),
3030
re_path(
31-
r"^search/(?P<organization_slug>[^\/]+)/(?P<integration_id>\d+)/$",
31+
r"^search/(?P<organization_id_or_slug>[^\/]+)/(?P<integration_id>\d+)/$",
3232
BitbucketSearchEndpoint.as_view(),
3333
name="sentry-extensions-bitbucket-search",
3434
),

src/sentry/integrations/github/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
name="sentry-integration-github-installation",
1717
),
1818
re_path(
19-
r"^search/(?P<organization_slug>[^\/]+)/(?P<integration_id>\d+)/$",
19+
r"^search/(?P<organization_id_or_slug>[^\/]+)/(?P<integration_id>\d+)/$",
2020
GithubSharedSearchEndpoint.as_view(),
2121
name="sentry-integration-github-search",
2222
),

src/sentry/integrations/gitlab/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
urlpatterns = [
77
re_path(
8-
r"^search/(?P<organization_slug>[^\/]+)/(?P<integration_id>\d+)/$",
8+
r"^search/(?P<organization_id_or_slug>[^\/]+)/(?P<integration_id>\d+)/$",
99
GitlabIssueSearchEndpoint.as_view(),
1010
name="sentry-extensions-gitlab-search",
1111
),

src/sentry/integrations/jira/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
name="sentry-extensions-jira-issue-updated",
4040
),
4141
re_path(
42-
r"^search/(?P<organization_slug>[^\/]+)/(?P<integration_id>\d+)/$",
42+
r"^search/(?P<organization_id_or_slug>[^\/]+)/(?P<integration_id>\d+)/$",
4343
JiraSearchEndpoint.as_view(),
4444
name="sentry-extensions-jira-search",
4545
),

src/sentry/integrations/jira_server/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
name="sentry-extensions-jiraserver-issue-updated",
1212
),
1313
re_path(
14-
r"^search/(?P<organization_slug>[^\/]+)/(?P<integration_id>\d+)/$",
14+
r"^search/(?P<organization_id_or_slug>[^\/]+)/(?P<integration_id>\d+)/$",
1515
JiraServerSearchEndpoint.as_view(),
1616
name="sentry-extensions-jiraserver-search",
1717
),

src/sentry/integrations/vsts/urls.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
name="sentry-extensions-vsts-issue-updated",
1313
),
1414
re_path(
15-
r"^search/(?P<organization_slug>[^\/]+)/(?P<integration_id>\d+)/$",
15+
r"^search/(?P<organization_id_or_slug>[^\/]+)/(?P<integration_id>\d+)/$",
1616
VstsSearchEndpoint.as_view(),
1717
name="sentry-extensions-vsts-search",
1818
),

src/sentry/monitors/endpoints/base.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class MonitorEndpoint(Endpoint):
5757
def convert_args(
5858
self,
5959
request: Request,
60-
organization_slug: str,
60+
organization_id_or_slug: int | str,
6161
monitor_id_or_slug: int | str,
6262
environment: str | None = None,
6363
checkin_id: str | None = None,
@@ -67,13 +67,13 @@ def convert_args(
6767
try:
6868
if (
6969
id_or_slug_path_params_enabled(
70-
self.convert_args.__qualname__, str(organization_slug)
70+
self.convert_args.__qualname__, str(organization_id_or_slug)
7171
)
72-
and str(organization_slug).isdigit()
72+
and str(organization_id_or_slug).isdigit()
7373
):
74-
organization = Organization.objects.get_from_cache(id=organization_slug)
74+
organization = Organization.objects.get_from_cache(id=organization_id_or_slug)
7575
else:
76-
organization = Organization.objects.get_from_cache(slug=organization_slug)
76+
organization = Organization.objects.get_from_cache(slug=organization_id_or_slug)
7777
except Organization.DoesNotExist:
7878
raise ResourceDoesNotExist
7979

src/sentry/monitors/endpoints/monitor_ingest_checkin_attachment.py

+19-15
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def convert_args(
6060
request: Request,
6161
monitor_id_or_slug: int | str,
6262
checkin_id: str,
63-
organization_slug: str | int | None = None,
63+
organization_id_or_slug: int | str | None = None,
6464
*args,
6565
**kwargs,
6666
):
@@ -83,24 +83,28 @@ def convert_args(
8383
raise ResourceDoesNotExist
8484
else:
8585

86-
# When using DSN auth we're able to infer the organization slug
87-
if not organization_slug and using_dsn_auth:
88-
organization_slug = request.auth.project.organization.slug
86+
# When using DSN auth we're able to infer the organization slug (organization_id_or_slug is slug in this case)
87+
if not organization_id_or_slug and using_dsn_auth:
88+
organization_id_or_slug = request.auth.project.organization.slug
8989

90-
# The only monitor endpoints that do not have the org slug in their
90+
# The only monitor endpoints that do not have the org id or slug in their
9191
# parameters are the GUID-style checkin endpoints
92-
if organization_slug:
92+
if organization_id_or_slug:
9393
try:
94-
# Try lookup by slug first. This requires organization context.
94+
# Try lookup by id or slug first. This requires organization context.
9595
if (
9696
id_or_slug_path_params_enabled(
97-
self.convert_args.__qualname__, str(organization_slug)
97+
self.convert_args.__qualname__, str(organization_id_or_slug)
9898
)
99-
and str(organization_slug).isdecimal()
99+
and str(organization_id_or_slug).isdecimal()
100100
):
101-
organization = Organization.objects.get_from_cache(id=organization_slug)
101+
organization = Organization.objects.get_from_cache(
102+
id=organization_id_or_slug
103+
)
102104
else:
103-
organization = Organization.objects.get_from_cache(slug=organization_slug)
105+
organization = Organization.objects.get_from_cache(
106+
slug=organization_id_or_slug
107+
)
104108

105109
monitor = get_monitor_by_org_id_or_slug(organization, monitor_id_or_slug)
106110
except (Organization.DoesNotExist, Monitor.DoesNotExist):
@@ -140,12 +144,12 @@ def convert_args(
140144
# When looking up via GUID we do not check the organization slug,
141145
# validate that the slug matches the org of the monitors project
142146

143-
# We only raise if the organization_slug was set and it doesn't match.
147+
# We only raise if the organization_id_or_slug was set and it doesn't match.
144148
# We don't check the api.id-or-slug-enabled option here because slug and id are unique
145149
if (
146-
organization_slug
147-
and project.organization.slug != organization_slug
148-
and project.organization.id != organization_slug
150+
organization_id_or_slug
151+
and project.organization.slug != organization_id_or_slug
152+
and project.organization.id != organization_id_or_slug
149153
):
150154
raise ResourceDoesNotExist
151155

src/sentry/monitors/endpoints/organization_monitor_checkin_index.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class OrganizationMonitorCheckInIndexEndpoint(MonitorEndpoint, MonitorCheckInMix
2727
@extend_schema(
2828
operation_id="Retrieve Check-Ins for a Monitor",
2929
parameters=[
30-
GlobalParams.ORG_SLUG,
30+
GlobalParams.ORG_ID_OR_SLUG,
3131
MonitorParams.MONITOR_ID_OR_SLUG,
3232
],
3333
responses={

src/sentry/monitors/endpoints/organization_monitor_details.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class OrganizationMonitorDetailsEndpoint(MonitorEndpoint, MonitorDetailsMixin):
3636
@extend_schema(
3737
operation_id="Retrieve a Monitor",
3838
parameters=[
39-
GlobalParams.ORG_SLUG,
39+
GlobalParams.ORG_ID_OR_SLUG,
4040
MonitorParams.MONITOR_ID_OR_SLUG,
4141
GlobalParams.ENVIRONMENT,
4242
],
@@ -56,7 +56,7 @@ def get(self, request: Request, organization, project, monitor) -> Response:
5656
@extend_schema(
5757
operation_id="Update a Monitor",
5858
parameters=[
59-
GlobalParams.ORG_SLUG,
59+
GlobalParams.ORG_ID_OR_SLUG,
6060
MonitorParams.MONITOR_ID_OR_SLUG,
6161
],
6262
request=MonitorValidator,
@@ -77,7 +77,7 @@ def put(self, request: AuthenticatedHttpRequest, organization, project, monitor)
7777
@extend_schema(
7878
operation_id="Delete a Monitor or Monitor Environments",
7979
parameters=[
80-
GlobalParams.ORG_SLUG,
80+
GlobalParams.ORG_ID_OR_SLUG,
8181
MonitorParams.MONITOR_ID_OR_SLUG,
8282
GlobalParams.ENVIRONMENT,
8383
],

src/sentry/monitors/endpoints/organization_monitor_environment_details.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class OrganizationMonitorEnvironmentDetailsEndpoint(
3535
@extend_schema(
3636
operation_id="Update a Monitor Environment",
3737
parameters=[
38-
GlobalParams.ORG_SLUG,
38+
GlobalParams.ORG_ID_OR_SLUG,
3939
MonitorParams.MONITOR_ID_OR_SLUG,
4040
MonitorParams.ENVIRONMENT,
4141
],
@@ -58,7 +58,7 @@ def put(
5858
@extend_schema(
5959
operation_id="Delete a Monitor Environments",
6060
parameters=[
61-
GlobalParams.ORG_SLUG,
61+
GlobalParams.ORG_ID_OR_SLUG,
6262
MonitorParams.MONITOR_ID_OR_SLUG,
6363
MonitorParams.ENVIRONMENT,
6464
],

0 commit comments

Comments
 (0)