From 218d554ff400d60d2e88e186bafb0013d8ddf7d6 Mon Sep 17 00:00:00 2001 From: Charlie Luo Date: Mon, 1 Jun 2026 11:18:15 -0700 Subject: [PATCH] feat(api-docs): publish source map debug endpoint Promote the source map debug endpoint (formerly the blue-thunder-edition variant, the one the frontend actually uses) from PRIVATE to PUBLIC with full OpenAPI docs: bump publish_status, add a response example, and register it at the canonical /events/{event_id}/source-map-debug/ URL. The legacy /source-map-debug-blue-thunder-edition/ URL is kept as an alias so the frontend (which hardcodes it) keeps working, and is excluded from the public schema via EXCLUSION_PATH_PREFIXES to avoid a duplicate path. A follow-up frontend PR will migrate to the canonical URL, after which the alias can be removed. Co-authored-by: Claude --- src/sentry/api/endpoints/source_map_debug.py | 4 +- src/sentry/api/urls.py | 6 ++ .../examples/source_map_debug_examples.py | 56 +++++++++++++++++++ src/sentry/apidocs/hooks.py | 2 + .../utils/api/knownSentryApiUrls.generated.ts | 1 + .../endpoints/events/test_source_map_debug.py | 28 ++++++++++ 6 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/sentry/apidocs/examples/source_map_debug_examples.py create mode 100644 tests/apidocs/endpoints/events/test_source_map_debug.py diff --git a/src/sentry/api/endpoints/source_map_debug.py b/src/sentry/api/endpoints/source_map_debug.py index 316875b9444323..3145657c0976b2 100644 --- a/src/sentry/api/endpoints/source_map_debug.py +++ b/src/sentry/api/endpoints/source_map_debug.py @@ -14,6 +14,7 @@ from sentry.api.base import cell_silo_endpoint from sentry.api.bases.project import ProjectEndpoint from sentry.apidocs.constants import RESPONSE_FORBIDDEN, RESPONSE_NOT_FOUND, RESPONSE_UNAUTHORIZED +from sentry.apidocs.examples.source_map_debug_examples import SourceMapDebugExamples from sentry.apidocs.parameters import EventParams, GlobalParams from sentry.apidocs.utils import inline_sentry_response_serializer from sentry.debug_files.release_files import maybe_renew_releasefiles @@ -128,7 +129,7 @@ class SourceMapDebugResponse(TypedDict): @extend_schema(tags=["Events"]) class SourceMapDebugEndpoint(ProjectEndpoint): publish_status = { - "GET": ApiPublishStatus.PRIVATE, + "GET": ApiPublishStatus.PUBLIC, } owner = ApiOwner.WEB_FRONTEND_SDKS @@ -147,6 +148,7 @@ class SourceMapDebugEndpoint(ProjectEndpoint): 403: RESPONSE_FORBIDDEN, 404: RESPONSE_NOT_FOUND, }, + examples=SourceMapDebugExamples.GET_SOURCE_MAP_DEBUG, ) def get(self, request: Request, project: Project, event_id: str) -> Response: """ diff --git a/src/sentry/api/urls.py b/src/sentry/api/urls.py index dcd2207b54c925..918564ccaf21bc 100644 --- a/src/sentry/api/urls.py +++ b/src/sentry/api/urls.py @@ -2766,6 +2766,12 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]: name="sentry-api-0-event-owners", ), re_path( + r"^(?P[^/]+)/(?P[^/]+)/events/(?P[^/]+)/source-map-debug/$", + SourceMapDebugEndpoint.as_view(), + name="sentry-api-0-event-source-map-debug", + ), + re_path( + # Legacy alias the frontend still hardcodes; remove once it migrates to source-map-debug/. r"^(?P[^/]+)/(?P[^/]+)/events/(?P[^/]+)/source-map-debug-blue-thunder-edition/$", SourceMapDebugEndpoint.as_view(), name="sentry-api-0-event-source-map-debug-blue-thunder-edition", diff --git a/src/sentry/apidocs/examples/source_map_debug_examples.py b/src/sentry/apidocs/examples/source_map_debug_examples.py new file mode 100644 index 00000000000000..87271d02ff2841 --- /dev/null +++ b/src/sentry/apidocs/examples/source_map_debug_examples.py @@ -0,0 +1,56 @@ +from drf_spectacular.utils import OpenApiExample + +SOURCE_MAP_DEBUG = { + "dist": "1.0.0", + "release": "frontend@1.0.0", + "exceptions": [ + { + "frames": [ + { + "debug_id_process": { + "debug_id": "f206e0e7-3d0c-41cb-bccc-11b716728e27", + "uploaded_source_file_with_correct_debug_id": True, + "uploaded_source_map_with_correct_debug_id": True, + }, + "release_process": { + "abs_path": "https://example.com/static/js/main.js", + "matching_source_file_names": ["~/static/js/main.js"], + "matching_source_map_name": "~/static/js/main.js.map", + "source_map_reference": "main.js.map", + "source_file_lookup_result": "found", + "source_map_lookup_result": "found", + }, + "scraping_process": { + "source_file": { + "url": "https://example.com/static/js/main.js", + "status": "success", + }, + "source_map": { + "url": "https://example.com/static/js/main.js.map", + "status": "success", + }, + }, + } + ] + } + ], + "has_debug_ids": True, + "min_debug_id_sdk_version": "7.56.0", + "sdk_version": "7.60.0", + "project_has_some_artifact_bundle": True, + "release_has_some_artifact": True, + "has_uploaded_some_artifact_with_a_debug_id": True, + "sdk_debug_id_support": "full", + "has_scraping_data": True, +} + + +class SourceMapDebugExamples: + GET_SOURCE_MAP_DEBUG = [ + OpenApiExample( + "Source map debug information for an event", + value=SOURCE_MAP_DEBUG, + response_only=True, + status_codes=["200"], + ) + ] diff --git a/src/sentry/apidocs/hooks.py b/src/sentry/apidocs/hooks.py index b46f48bd61e0cc..010af18b0704ec 100644 --- a/src/sentry/apidocs/hooks.py +++ b/src/sentry/apidocs/hooks.py @@ -38,6 +38,8 @@ class EndpointRegistryType(TypedDict): "/api/0/monitors/", # Issue URLS have an expression of group|issue that resolves to `var` "/api/0/{var}/{issue_id}/", + # Legacy alias of source-map-debug/; documented only at the canonical path. + "/api/0/projects/{organization_id_or_slug}/{project_id_or_slug}/events/{event_id}/source-map-debug-blue-thunder-edition/", ] diff --git a/static/app/utils/api/knownSentryApiUrls.generated.ts b/static/app/utils/api/knownSentryApiUrls.generated.ts index e7066df5ad5d1d..c5def5868f8790 100644 --- a/static/app/utils/api/knownSentryApiUrls.generated.ts +++ b/static/app/utils/api/knownSentryApiUrls.generated.ts @@ -626,6 +626,7 @@ export type KnownSentryApiUrls = | '/projects/$organizationIdOrSlug/$projectIdOrSlug/events/$eventId/owners/' | '/projects/$organizationIdOrSlug/$projectIdOrSlug/events/$eventId/reprocessable/' | '/projects/$organizationIdOrSlug/$projectIdOrSlug/events/$eventId/source-map-debug-blue-thunder-edition/' + | '/projects/$organizationIdOrSlug/$projectIdOrSlug/events/$eventId/source-map-debug/' | '/projects/$organizationIdOrSlug/$projectIdOrSlug/files/artifact-bundles/' | '/projects/$organizationIdOrSlug/$projectIdOrSlug/files/difs/assemble/' | '/projects/$organizationIdOrSlug/$projectIdOrSlug/files/dsyms/' diff --git a/tests/apidocs/endpoints/events/test_source_map_debug.py b/tests/apidocs/endpoints/events/test_source_map_debug.py new file mode 100644 index 00000000000000..cf6d8579eb75ba --- /dev/null +++ b/tests/apidocs/endpoints/events/test_source_map_debug.py @@ -0,0 +1,28 @@ +from django.test.client import RequestFactory +from django.urls import reverse + +from fixtures.apidocs_test_case import APIDocsTestCase + + +class SourceMapDebugDocs(APIDocsTestCase): + endpoint = "sentry-api-0-event-source-map-debug" + + def setUp(self) -> None: + event = self.create_event("a") + + self.url = reverse( + self.endpoint, + kwargs={ + "organization_id_or_slug": self.project.organization.slug, + "project_id_or_slug": self.project.slug, + "event_id": event.event_id, + }, + ) + + self.login_as(user=self.user) + + def test_get(self) -> None: + response = self.client.get(self.url) + request = RequestFactory().get(self.url) + + self.validate_schema(request, response)