Skip to content

Commit d0c4d9b

Browse files
scttcperclaude
andcommitted
perf(api): Add collapse=organization to project details endpoint
The project details endpoint serializes the full organization through OrganizationSummarySerializer which fetches avatars, auth provider config, and computes feature flags. When the caller only needs the org id and slug, `?collapse=organization` skips all of that and returns a minimal object. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ca77ff5 commit d0c4d9b

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
LATEST_DEPLOYS_KEY: Final = "latestDeploys"
7777
UNUSED_ON_FRONTEND_FEATURES: Final = "unusedFeatures"
78+
ORGANIZATION_KEY: Final = "organization"
7879

7980

8081
# These features are not used on the frontend,
@@ -979,7 +980,16 @@ def get_attrs(
979980
for option in queryset.iterator():
980981
options_by_project[option.project_id][option.key] = option.value
981982

982-
orgs = {d["id"]: d for d in serialize(list({i.organization for i in item_list}), user)}
983+
if self._collapse(ORGANIZATION_KEY):
984+
orgs = {
985+
str(i.organization_id): {
986+
"id": str(i.organization_id),
987+
"slug": i.organization.slug,
988+
}
989+
for i in item_list
990+
}
991+
else:
992+
orgs = {d["id"]: d for d in serialize(list({i.organization for i in item_list}), user)}
983993

984994
# Only fetch the latest release version key for each project to cut down on response size
985995
latest_release_versions = _get_project_to_release_version_mapping(item_list)

src/sentry/core/endpoints/project_details.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,8 @@ def get(self, request: Request, project: Project) -> Response:
549549
"""
550550
Return details on an individual project.
551551
"""
552-
data = serialize(project, request.user, DetailedProjectSerializer())
552+
collapse = request.GET.getlist("collapse", [])
553+
data = serialize(project, request.user, DetailedProjectSerializer(collapse=collapse))
553554

554555
# TODO: should switch to expand and move logic into the serializer
555556
include = set(filter(bool, request.GET.get("include", "").split(",")))

tests/sentry/core/endpoints/test_project_details.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,17 @@ def test_no_alert_integration(self) -> None:
115115
)
116116
assert not response.data["hasAlertIntegrationInstalled"]
117117

118+
def test_collapse_organization(self) -> None:
119+
response = self.get_success_response(
120+
self.project.organization.slug,
121+
self.project.slug,
122+
qs_params={"collapse": ["organization"]},
123+
)
124+
assert response.data["organization"] == {
125+
"id": str(self.project.organization.id),
126+
"slug": self.project.organization.slug,
127+
}
128+
118129
def test_filters_disabled_plugins(self) -> None:
119130
from sentry.plugins.base import plugins
120131

0 commit comments

Comments
 (0)