Skip to content

Commit a3f1cb6

Browse files
billyvgclaude
andcommitted
feat(replays): Make bulk delete API endpoints public
Change publish_status from PRIVATE to PUBLIC on the replay deletion job endpoints (index and detail) and add @extend_schema decorators with operation IDs, response types, and OpenAPI examples so that drf-spectacular generates documentation for them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f9236eb commit a3f1cb6

File tree

2 files changed

+143
-3
lines changed

2 files changed

+143
-3
lines changed

src/sentry/apidocs/examples/replay_examples.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,71 @@ class ReplayExamples:
200200
)
201201
]
202202

203+
GET_REPLAY_DELETION_JOBS = [
204+
OpenApiExample(
205+
"List replay deletion jobs",
206+
value={
207+
"data": [
208+
{
209+
"id": 1,
210+
"dateCreated": "2024-01-01T00:00:00Z",
211+
"dateUpdated": "2024-01-01T00:05:00Z",
212+
"rangeStart": "2023-12-01T00:00:00Z",
213+
"rangeEnd": "2024-01-01T00:00:00Z",
214+
"environments": ["production"],
215+
"status": "pending",
216+
"query": "user.email:test@example.com",
217+
"countDeleted": 0,
218+
}
219+
]
220+
},
221+
status_codes=["200"],
222+
response_only=True,
223+
)
224+
]
225+
226+
CREATE_REPLAY_DELETION_JOB = [
227+
OpenApiExample(
228+
"Create a replay deletion job",
229+
value={
230+
"data": {
231+
"id": 1,
232+
"dateCreated": "2024-01-01T00:00:00Z",
233+
"dateUpdated": "2024-01-01T00:05:00Z",
234+
"rangeStart": "2023-12-01T00:00:00Z",
235+
"rangeEnd": "2024-01-01T00:00:00Z",
236+
"environments": ["production"],
237+
"status": "pending",
238+
"query": "user.email:test@example.com",
239+
"countDeleted": 0,
240+
}
241+
},
242+
status_codes=["201"],
243+
response_only=True,
244+
)
245+
]
246+
247+
GET_REPLAY_DELETION_JOB = [
248+
OpenApiExample(
249+
"Get a replay deletion job",
250+
value={
251+
"data": {
252+
"id": 1,
253+
"dateCreated": "2024-01-01T00:00:00Z",
254+
"dateUpdated": "2024-01-01T00:05:00Z",
255+
"rangeStart": "2023-12-01T00:00:00Z",
256+
"rangeEnd": "2024-01-01T00:00:00Z",
257+
"environments": ["production"],
258+
"status": "pending",
259+
"query": "user.email:test@example.com",
260+
"countDeleted": 0,
261+
}
262+
},
263+
status_codes=["200"],
264+
response_only=True,
265+
)
266+
]
267+
203268
GET_REPLAY_VIEWED_BY = [
204269
OpenApiExample(
205270
"Get list of users who have viewed a replay",

src/sentry/replays/endpoints/project_replay_jobs_delete.py

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
from __future__ import annotations
2+
3+
from typing import TypedDict
4+
5+
from drf_spectacular.utils import extend_schema
16
from rest_framework import serializers
27
from rest_framework.request import Request
38
from rest_framework.response import Response
@@ -10,12 +15,36 @@
1015
from sentry.api.exceptions import ResourceDoesNotExist
1116
from sentry.api.paginator import OffsetPaginator
1217
from sentry.api.serializers import Serializer, serialize
18+
from sentry.apidocs.constants import RESPONSE_BAD_REQUEST, RESPONSE_FORBIDDEN, RESPONSE_NOT_FOUND
19+
from sentry.apidocs.examples.replay_examples import ReplayExamples
20+
from sentry.apidocs.parameters import GlobalParams
21+
from sentry.apidocs.utils import inline_sentry_response_serializer
1322
from sentry.replays.endpoints.project_replay_endpoint import ProjectReplayEndpoint
1423
from sentry.replays.models import ReplayDeletionJobModel
1524
from sentry.replays.permissions import has_replay_permission
1625
from sentry.replays.tasks import run_bulk_replay_delete_job
1726

1827

28+
class ReplayDeletionJobResponseData(TypedDict):
29+
id: int
30+
dateCreated: str
31+
dateUpdated: str
32+
rangeStart: str
33+
rangeEnd: str
34+
environments: list[str]
35+
status: str
36+
query: str
37+
countDeleted: int
38+
39+
40+
class ReplayDeletionJobListResponse(TypedDict):
41+
data: list[ReplayDeletionJobResponseData]
42+
43+
44+
class ReplayDeletionJobDetailResponse(TypedDict):
45+
data: ReplayDeletionJobResponseData
46+
47+
1948
class ReplayDeletionJobPermission(ProjectPermission):
2049
scope_map = {
2150
"GET": ["project:write", "project:admin"],
@@ -57,14 +86,29 @@ class ReplayDeletionJobCreateSerializer(serializers.Serializer):
5786

5887

5988
@cell_silo_endpoint
89+
@extend_schema(tags=["Replays"])
6090
class ProjectReplayDeletionJobsIndexEndpoint(ProjectEndpoint):
6191
owner = ApiOwner.DATA_BROWSING
6292
publish_status = {
63-
"GET": ApiPublishStatus.PRIVATE,
64-
"POST": ApiPublishStatus.PRIVATE,
93+
"GET": ApiPublishStatus.PUBLIC,
94+
"POST": ApiPublishStatus.PUBLIC,
6595
}
6696
permission_classes = (ReplayDeletionJobPermission,)
6797

98+
@extend_schema(
99+
operation_id="List Replay Deletion Jobs",
100+
parameters=[
101+
GlobalParams.ORG_ID_OR_SLUG,
102+
GlobalParams.PROJECT_ID_OR_SLUG,
103+
],
104+
responses={
105+
200: inline_sentry_response_serializer(
106+
"ListReplayDeletionJobs", ReplayDeletionJobListResponse
107+
),
108+
403: RESPONSE_FORBIDDEN,
109+
},
110+
examples=ReplayExamples.GET_REPLAY_DELETION_JOBS,
111+
)
68112
def get(self, request: Request, project) -> Response:
69113
"""
70114
Retrieve a collection of replay delete jobs.
@@ -86,6 +130,21 @@ def get(self, request: Request, project) -> Response:
86130
paginator_cls=OffsetPaginator,
87131
)
88132

133+
@extend_schema(
134+
operation_id="Create a Replay Deletion Job",
135+
parameters=[
136+
GlobalParams.ORG_ID_OR_SLUG,
137+
GlobalParams.PROJECT_ID_OR_SLUG,
138+
],
139+
responses={
140+
201: inline_sentry_response_serializer(
141+
"CreateReplayDeletionJob", ReplayDeletionJobDetailResponse
142+
),
143+
400: RESPONSE_BAD_REQUEST,
144+
403: RESPONSE_FORBIDDEN,
145+
},
146+
examples=ReplayExamples.CREATE_REPLAY_DELETION_JOB,
147+
)
89148
def post(self, request: Request, project) -> Response:
90149
"""
91150
Create a new replay deletion job.
@@ -132,12 +191,28 @@ def post(self, request: Request, project) -> Response:
132191

133192

134193
@cell_silo_endpoint
194+
@extend_schema(tags=["Replays"])
135195
class ProjectReplayDeletionJobDetailEndpoint(ProjectReplayEndpoint):
136196
publish_status = {
137-
"GET": ApiPublishStatus.PRIVATE,
197+
"GET": ApiPublishStatus.PUBLIC,
138198
}
139199
permission_classes = (ReplayDeletionJobPermission,)
140200

201+
@extend_schema(
202+
operation_id="Get a Replay Deletion Job",
203+
parameters=[
204+
GlobalParams.ORG_ID_OR_SLUG,
205+
GlobalParams.PROJECT_ID_OR_SLUG,
206+
],
207+
responses={
208+
200: inline_sentry_response_serializer(
209+
"GetReplayDeletionJob", ReplayDeletionJobDetailResponse
210+
),
211+
403: RESPONSE_FORBIDDEN,
212+
404: RESPONSE_NOT_FOUND,
213+
},
214+
examples=ReplayExamples.GET_REPLAY_DELETION_JOB,
215+
)
141216
def get(self, request: Request, project, job_id: int) -> Response:
142217
"""
143218
Fetch a replay delete job instance.

0 commit comments

Comments
 (0)