Skip to content

Commit 182b152

Browse files
feat(launchpad): create size analysis download endpoint (#95089)
<!-- Describe your PR here. --> <!-- Sentry employees and contractors can delete or ignore the following. --> ### Legal Boilerplate Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.
1 parent b5d1450 commit 182b152

File tree

3 files changed

+115
-0
lines changed

3 files changed

+115
-0
lines changed

src/sentry/preprod/analytics.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ class PreprodArtifactApiAssembleGenericEvent(analytics.Event):
2929
)
3030

3131

32+
class PreprodArtifactApiSizeAnalysisDownloadEvent(analytics.Event):
33+
type = "preprod_artifact.api.size_analysis_download"
34+
35+
attributes = (
36+
analytics.Attribute("organization_id"),
37+
analytics.Attribute("project_id"),
38+
analytics.Attribute("user_id", required=False),
39+
analytics.Attribute("artifact_id"),
40+
)
41+
42+
3243
analytics.register(PreprodArtifactApiAssembleEvent)
3344
analytics.register(PreprodArtifactApiUpdateEvent)
3445
analytics.register(PreprodArtifactApiAssembleGenericEvent)
46+
analytics.register(PreprodArtifactApiSizeAnalysisDownloadEvent)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from django.conf import settings
2+
from django.http.response import FileResponse, HttpResponseBase
3+
from rest_framework.request import Request
4+
from rest_framework.response import Response
5+
6+
from sentry import analytics, features
7+
from sentry.api.api_owners import ApiOwner
8+
from sentry.api.api_publish_status import ApiPublishStatus
9+
from sentry.api.base import region_silo_endpoint
10+
from sentry.api.bases.project import ProjectEndpoint
11+
from sentry.models.files.file import File
12+
from sentry.preprod.models import PreprodArtifactSizeMetrics
13+
14+
15+
@region_silo_endpoint
16+
class ProjectPreprodArtifactSizeAnalysisDownloadEndpoint(ProjectEndpoint):
17+
owner = ApiOwner.EMERGE_TOOLS
18+
publish_status = {
19+
"GET": ApiPublishStatus.EXPERIMENTAL,
20+
}
21+
22+
def get(self, request: Request, project, artifact_id) -> HttpResponseBase:
23+
"""
24+
Download size analysis results for a preprod artifact
25+
````````````````````````````````````````````````````
26+
27+
Download the size analysis results for a preprod artifact.
28+
29+
:pparam string organization_id_or_slug: the id or slug of the organization the
30+
artifact belongs to.
31+
:pparam string project_id_or_slug: the id or slug of the project to retrieve the
32+
artifact from.
33+
:pparam string artifact_id: the ID of the preprod artifact to download size analysis for.
34+
:auth: required
35+
"""
36+
37+
analytics.record(
38+
"preprod_artifact.api.size_analysis_download",
39+
organization_id=project.organization_id,
40+
project_id=project.id,
41+
user_id=request.user.id,
42+
artifact_id=artifact_id,
43+
)
44+
45+
if not settings.IS_DEV and not features.has(
46+
"organizations:preprod-artifact-assemble", project.organization, actor=request.user
47+
):
48+
return Response({"error": "Feature not enabled"}, status=403)
49+
50+
try:
51+
size_metrics_qs = PreprodArtifactSizeMetrics.objects.select_related(
52+
"preprod_artifact"
53+
).filter(
54+
preprod_artifact__project=project,
55+
preprod_artifact__id=artifact_id,
56+
)
57+
size_metrics_count = size_metrics_qs.count()
58+
if size_metrics_count == 0:
59+
return Response(
60+
{"error": "Preprod artifact not found or size analysis results not available"},
61+
status=404,
62+
)
63+
elif size_metrics_count > 1:
64+
return Response(
65+
{"error": "Multiple size analysis results found for this artifact"},
66+
status=409,
67+
)
68+
size_metrics = size_metrics_qs.first()
69+
except Exception:
70+
return Response(
71+
{"error": "Failed to retrieve size analysis results"},
72+
status=500,
73+
)
74+
75+
if size_metrics is None or size_metrics.analysis_file_id is None:
76+
return Response(
77+
{"error": "Size analysis file not available for this artifact"}, status=404
78+
)
79+
80+
try:
81+
file_obj = File.objects.get(id=size_metrics.analysis_file_id)
82+
except File.DoesNotExist:
83+
return Response({"error": "Size analysis file not found"}, status=404)
84+
85+
try:
86+
fp = file_obj.getfile()
87+
except Exception:
88+
return Response({"error": "Failed to retrieve size analysis file"}, status=500)
89+
90+
response = FileResponse(
91+
fp,
92+
content_type="application/json",
93+
)
94+
response["Content-Length"] = file_obj.size
95+
return response

src/sentry/preprod/api/endpoints/urls.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from .organization_preprod_artifact_assemble import ProjectPreprodArtifactAssembleEndpoint
44
from .project_preprod_artifact_assemble_generic import ProjectPreprodArtifactAssembleGenericEndpoint
55
from .project_preprod_artifact_download import ProjectPreprodArtifactDownloadEndpoint
6+
from .project_preprod_artifact_size_analysis_download import (
7+
ProjectPreprodArtifactSizeAnalysisDownloadEndpoint,
8+
)
69
from .project_preprod_artifact_update import ProjectPreprodArtifactUpdateEndpoint
710

811
preprod_urlpatterns = [
@@ -11,6 +14,11 @@
1114
ProjectPreprodArtifactAssembleEndpoint.as_view(),
1215
name="sentry-api-0-assemble-preprod-artifact-files",
1316
),
17+
re_path(
18+
r"^(?P<organization_id_or_slug>[^/]+)/(?P<project_id_or_slug>[^/]+)/files/preprodartifacts/(?P<artifact_id>[^/]+)/size-analysis/$",
19+
ProjectPreprodArtifactSizeAnalysisDownloadEndpoint.as_view(),
20+
name="sentry-api-0-project-preprod-artifact-size-analysis-download",
21+
),
1422
]
1523

1624
preprod_internal_urlpatterns = [

0 commit comments

Comments
 (0)