Browse Source

feat(semver): Allow `OrganizationReleasesStatsEndpoint` to be filtered by `query` (#26949)

This adds the same querying functionality from `OrganizationReleasesEndpoint` to
`OrganizationReleasesStatsEndpoint`. This should allow querying by wildcard matching and also
semver.
Dan Fuller 3 years ago
parent
commit
8cb94860cc

+ 24 - 16
src/sentry/api/endpoints/organization_releases.py

@@ -66,6 +66,25 @@ def add_date_filter_to_queryset(queryset, filter_params):
     return queryset
 
 
+def _filter_releases_by_query(queryset, organization, query):
+    search_filters = parse_search_query(query)
+    for search_filter in search_filters:
+        if search_filter.key.name == RELEASE_FREE_TEXT_KEY:
+            query_q = Q(version__icontains=query)
+            suffix_match = _release_suffix.match(query)
+            if suffix_match is not None:
+                query_q |= Q(version__icontains="%s+%s" % suffix_match.groups())
+
+            queryset = queryset.filter(query_q)
+
+        if search_filter.key.name == SEMVER_ALIAS:
+            queryset = queryset.filter_by_semver(
+                organization.id,
+                parse_semver(search_filter.value.raw_value, search_filter.operator),
+            )
+    return queryset
+
+
 class ReleaseSerializerWithProjects(ReleaseWithVersionSerializer):
     projects = ListField()
     headCommits = ListField(
@@ -204,22 +223,7 @@ class OrganizationReleasesEndpoint(
         queryset = add_environment_to_queryset(queryset, filter_params)
 
         if query:
-            search_filters = parse_search_query(query)
-            # TODO: Handle semver here as well
-            for search_filter in search_filters:
-                if search_filter.key.name == RELEASE_FREE_TEXT_KEY:
-                    query_q = Q(version__icontains=query)
-                    suffix_match = _release_suffix.match(query)
-                    if suffix_match is not None:
-                        query_q |= Q(version__icontains="%s+%s" % suffix_match.groups())
-
-                    queryset = queryset.filter(query_q)
-
-                if search_filter.key.name == SEMVER_ALIAS:
-                    queryset = queryset.filter_by_semver(
-                        organization.id,
-                        parse_semver(search_filter.value.raw_value, search_filter.operator),
-                    )
+            queryset = _filter_releases_by_query(queryset, organization, query)
 
         select_extra = {}
 
@@ -457,6 +461,8 @@ class OrganizationReleasesStatsEndpoint(OrganizationReleasesBaseEndpoint, Enviro
 
         :pparam string organization_slug: the organization short name
         """
+        query = request.GET.get("query")
+
         try:
             filter_params = self.get_filter_params(request, organization, date_filter_optional=True)
         except NoProjects:
@@ -476,6 +482,8 @@ class OrganizationReleasesStatsEndpoint(OrganizationReleasesBaseEndpoint, Enviro
 
         queryset = add_date_filter_to_queryset(queryset, filter_params)
         queryset = add_environment_to_queryset(queryset, filter_params)
+        if query:
+            queryset = _filter_releases_by_query(queryset, organization, query)
 
         return self.paginate(
             request=request,

+ 37 - 0
tests/sentry/api/endpoints/test_organization_releases.py

@@ -399,6 +399,8 @@ class OrganizationReleaseListTest(APITestCase):
 
 
 class OrganizationReleaseStatsTest(APITestCase):
+    endpoint = "sentry-api-0-organization-releases-stats"
+
     def setUp(self):
         self.project1 = self.create_project(teams=[self.team], organization=self.organization)
         self.project2 = self.create_project(teams=[self.team], organization=self.organization)
@@ -533,6 +535,41 @@ class OrganizationReleaseStatsTest(APITestCase):
             assert len(response.data) == 1
             assert "adoptionStages" in response.data[0]
 
+    def test_semver_filter(self):
+        self.login_as(user=self.user)
+
+        release_1 = self.create_release(version="test@1.2.4")
+        release_2 = self.create_release(version="test@1.2.3")
+        self.create_release(version="some.release")
+
+        response = self.get_valid_response(self.organization.slug, query=f"{SEMVER_ALIAS}:>1.2.3")
+        assert [r["version"] for r in response.data] == [release_1.version]
+
+        response = self.get_valid_response(self.organization.slug, query=f"{SEMVER_ALIAS}:>=1.2.3")
+        assert [r["version"] for r in response.data] == [release_2.version, release_1.version]
+
+        response = self.get_valid_response(self.organization.slug, query=f"{SEMVER_ALIAS}:1.2.*")
+        assert [r["version"] for r in response.data] == [release_2.version, release_1.version]
+
+        response = self.get_valid_response(self.organization.slug, query=f"{SEMVER_ALIAS}:2.2.1")
+        assert [r["version"] for r in response.data] == []
+
+    def test_query_filter(self):
+        self.login_as(user=self.user)
+
+        release = self.create_release(
+            self.project, version="foobar", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
+        )
+        self.create_release(
+            self.project, version="sdfsdfsdf", date_added=datetime(2013, 8, 13, 3, 8, 24, 880386)
+        )
+
+        response = self.get_valid_response(self.organization.slug, query="oob")
+        assert [r["version"] for r in response.data] == [release.version]
+
+        response = self.get_valid_response(self.organization.slug, query="baz")
+        assert [r["version"] for r in response.data] == []
+
 
 class OrganizationReleaseCreateTest(APITestCase):
     def test_minimal(self):