Skip to content

Commit 1cca280

Browse files
chore(preprod): Filter out mobile builds if any related size status is NOT_RAN (#107885)
Close https://linear.app/getsentry/issue/EME-801/hide-builds-with-not-ran-size-analysis-from-size-table
1 parent 8a4fa6a commit 1cca280

3 files changed

Lines changed: 102 additions & 2 deletions

File tree

src/sentry/preprod/artifact_search.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
from sentry.db.models.fields.bounded import I64_MAX
1616
from sentry.exceptions import InvalidSearchQuery
1717
from sentry.models.organization import Organization
18-
from sentry.preprod.models import PreprodArtifact, PreprodArtifactQuerySet
18+
from sentry.preprod.models import (
19+
PreprodArtifact,
20+
PreprodArtifactQuerySet,
21+
PreprodArtifactSizeMetrics,
22+
)
1923

2024
search_config = SearchConfig.create_from(
2125
SearchConfig(),
@@ -63,6 +67,7 @@
6367
"installable",
6468
"is",
6569
"platform_name",
70+
"size_state",
6671
},
6772
# Enable boolean operators
6873
# allow_boolean=True,
@@ -96,9 +101,24 @@ def get_field_type(key: str) -> str | None:
96101
"git_head_ref": "commit_comparison__head_ref",
97102
"git_head_sha": "commit_comparison__head_sha",
98103
"git_pr_number": "commit_comparison__pr_number",
104+
"size_state": "preprodartifactsizemetrics__state",
105+
}
106+
107+
SIZE_STATE_VALUES: dict[str, int] = {
108+
member.name.lower(): member.value for member in PreprodArtifactSizeMetrics.SizeAnalysisState
99109
}
100110

101111

112+
def _translate_size_state(value: object) -> int:
113+
raw = str(value).lower()
114+
if raw not in SIZE_STATE_VALUES:
115+
raise InvalidSearchQuery(
116+
f"Invalid size_state value: {value}. "
117+
f"Valid values: {', '.join(sorted(SIZE_STATE_VALUES))}"
118+
)
119+
return SIZE_STATE_VALUES[raw]
120+
121+
102122
def queryset_for_query(
103123
query: str,
104124
organization: Organization,
@@ -224,6 +244,16 @@ def apply_filters(
224244

225245
db_field = FIELD_MAPPINGS.get(name, name)
226246

247+
if name == "size_state":
248+
values = token.value.value if token.is_in_filter else [token.value.value]
249+
q = Q(**{f"{db_field}__in": [_translate_size_state(v) for v in values]})
250+
if token.is_negation:
251+
queryset = queryset.exclude(q)
252+
else:
253+
queryset = queryset.filter(q)
254+
queryset = queryset.distinct()
255+
continue
256+
227257
# We don't have to handle boolean operators or parens here
228258
# since allow_boolean is not set in SearchConfig.
229259
if token.is_in_filter:

tests/sentry/preprod/api/endpoints/test_builds.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.urls import reverse
55
from django.utils.functional import cached_property
66

7+
from sentry.preprod.models import PreprodArtifactSizeMetrics
78
from sentry.testutils.cases import APITestCase
89
from sentry.testutils.helpers.datetime import before_now
910
from sentry.testutils.helpers.features import with_feature
@@ -845,6 +846,57 @@ def test_free_text_search_with_structured_filter(self) -> None:
845846
assert len(data) == 1
846847
assert data[0]["app_info"]["app_id"] == "com.example.android"
847848

849+
@with_feature("organizations:preprod-frontend-routes")
850+
def test_size_state_filter(self) -> None:
851+
artifact_not_ran = self.create_preprod_artifact(app_id="not_ran.app")
852+
self.create_preprod_artifact_size_metrics(
853+
artifact_not_ran,
854+
state=PreprodArtifactSizeMetrics.SizeAnalysisState.NOT_RAN,
855+
error_code=PreprodArtifactSizeMetrics.ErrorCode.SKIPPED,
856+
error_message="Size analysis not supported",
857+
)
858+
artifact_completed = self.create_preprod_artifact(app_id="completed.app")
859+
self.create_preprod_artifact_size_metrics(
860+
artifact_completed, state=PreprodArtifactSizeMetrics.SizeAnalysisState.COMPLETED
861+
)
862+
self.create_preprod_artifact(app_id="no_metrics.app")
863+
864+
response = self._request({})
865+
self._assert_is_successful(response)
866+
assert len(response.json()) == 3
867+
868+
response = self._request({"query": "size_state:completed"})
869+
self._assert_is_successful(response)
870+
assert len(response.json()) == 1
871+
assert response.json()[0]["app_info"]["app_id"] == "completed.app"
872+
873+
response = self._request({"query": "!size_state:not_ran"})
874+
self._assert_is_successful(response)
875+
assert len(response.json()) == 2
876+
877+
@with_feature("organizations:preprod-frontend-routes")
878+
def test_size_state_filter_mixed_metrics(self) -> None:
879+
artifact = self.create_preprod_artifact(app_id="mixed.app")
880+
self.create_preprod_artifact_size_metrics(
881+
artifact, state=PreprodArtifactSizeMetrics.SizeAnalysisState.COMPLETED
882+
)
883+
self.create_preprod_artifact_size_metrics(
884+
artifact,
885+
state=PreprodArtifactSizeMetrics.SizeAnalysisState.NOT_RAN,
886+
metrics_type=PreprodArtifactSizeMetrics.MetricsArtifactType.WATCH_ARTIFACT,
887+
error_code=PreprodArtifactSizeMetrics.ErrorCode.SKIPPED,
888+
error_message="Size analysis not supported",
889+
)
890+
response = self._request({"query": "!size_state:not_ran"})
891+
self._assert_is_successful(response)
892+
assert len(response.json()) == 0
893+
894+
@with_feature("organizations:preprod-frontend-routes")
895+
def test_size_state_invalid_values(self) -> None:
896+
self.create_preprod_artifact(app_id="test.app")
897+
assert self._request({"query": "size_state:bogus"}).status_code == 400
898+
assert self._request({"query": "size_state:[bogus, completed]"}).status_code == 400
899+
848900

849901
class QuerysetForQueryTest(APITestCase):
850902
"""Tests for the queryset_for_query, artifact_in_queryset, and artifact_matches_query functions."""

tests/sentry/preprod/api/endpoints/test_organization_preprod_list_builds.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from sentry import analytics
99
from sentry.preprod.analytics import PreprodArtifactApiListBuildsEvent
10-
from sentry.preprod.models import PreprodArtifact
10+
from sentry.preprod.models import PreprodArtifact, PreprodArtifactSizeMetrics
1111
from sentry.testutils.cases import APITestCase
1212

1313

@@ -742,3 +742,21 @@ def test_list_builds_filter_project_and_other_criteria(self) -> None:
742742
builds = response.json()["builds"]
743743
assert len(builds) == 1
744744
assert builds[0]["app_info"]["app_id"] == "com.example.app4"
745+
746+
def test_list_builds_does_not_filter_by_size_state(self) -> None:
747+
self.create_preprod_artifact_size_metrics(
748+
self.artifact1,
749+
state=PreprodArtifactSizeMetrics.SizeAnalysisState.NOT_RAN,
750+
error_code=PreprodArtifactSizeMetrics.ErrorCode.SKIPPED,
751+
error_message="Size analysis was not requested",
752+
)
753+
response = self.client.get(
754+
f"{self._get_url()}?project={self.project.id}&project={self.project2.id}",
755+
format="json",
756+
HTTP_AUTHORIZATION=f"Bearer {self.api_token.token}",
757+
)
758+
assert response.status_code == 200
759+
builds = response.json()["builds"]
760+
assert len(builds) == 4
761+
app_ids = {b["app_info"]["app_id"] for b in builds}
762+
assert "com.example.app" in app_ids

0 commit comments

Comments
 (0)