Browse Source

feat(similarity-embedding): Add endpoint to call backfill script (#68751)

Create endpoint to call the backfill script
[here](https://github.com/getsentry/sentry/pull/68466)

---------

Co-authored-by: Josh Ferge <josh.ferge@sentry.io>
Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Co-authored-by: Katie Byers <katie.byers@sentry.io>
Jodi Jang 10 months ago
parent
commit
b6b4d614f3

+ 31 - 0
src/sentry/api/endpoints/project_backfill_similar_issues_embeddings_records.py

@@ -0,0 +1,31 @@
+from rest_framework.request import Request
+from rest_framework.response import Response
+
+from sentry import features
+from sentry.api.api_owners import ApiOwner
+from sentry.api.api_publish_status import ApiPublishStatus
+from sentry.api.base import region_silo_endpoint
+from sentry.api.bases.project import ProjectEndpoint
+from sentry.auth.superuser import is_active_superuser
+from sentry.tasks.backfill_seer_grouping_records import backfill_seer_grouping_records
+
+
+@region_silo_endpoint
+class ProjectBackfillSimilarIssuesEmbeddingsRecords(ProjectEndpoint):
+    owner = ApiOwner.ISSUES
+    publish_status = {
+        "POST": ApiPublishStatus.PRIVATE,
+    }
+
+    def post(self, request: Request, project) -> Response:
+        if not features.has(
+            "projects:similarity-embeddings-backfill", project
+        ) or not is_active_superuser(request):
+            return Response(status=404)
+
+        last_processed_id = None
+        if request.data.get("last_processed_id"):
+            last_processed_id = int(request.data["last_processed_id"])
+
+        backfill_seer_grouping_records.delay(project.id, last_processed_id)
+        return Response(status=204)

+ 8 - 0
src/sentry/api/urls.py

@@ -28,6 +28,9 @@ from sentry.api.endpoints.organization_unsubscribe import (
     OrganizationUnsubscribeIssue,
     OrganizationUnsubscribeProject,
 )
+from sentry.api.endpoints.project_backfill_similar_issues_embeddings_records import (
+    ProjectBackfillSimilarIssuesEmbeddingsRecords,
+)
 from sentry.api.endpoints.project_stacktrace_coverage import ProjectStacktraceCoverageEndpoint
 from sentry.api.endpoints.project_statistical_detectors import ProjectStatisticalDetectors
 from sentry.api.endpoints.release_thresholds.release_threshold import ReleaseThresholdEndpoint
@@ -2541,6 +2544,11 @@ PROJECT_URLS: list[URLPattern | URLResolver] = [
         ProjectRuleTaskDetailsEndpoint.as_view(),
         name="sentry-api-0-project-rule-task-details",
     ),
+    re_path(
+        r"^(?P<organization_slug>[^\/]+)/(?P<project_slug>[^\/]+)/backfill-similar-embeddings-records/$",
+        ProjectBackfillSimilarIssuesEmbeddingsRecords.as_view(),
+        name="sentry-api-0-project-backfill-similar-embeddings-records",
+    ),
     re_path(
         r"^(?P<organization_id_or_slug>[^\/]+)/(?P<project_id_or_slug>[^\/]+)/stats/$",
         ProjectStatsEndpoint.as_view(),

+ 63 - 0
tests/sentry/api/endpoints/test_project_backfill_similar_issues_embeddings_records.py

@@ -0,0 +1,63 @@
+from unittest.mock import patch
+
+from django.urls import reverse
+
+from sentry.testutils.cases import APITestCase
+from sentry.testutils.helpers.features import with_feature
+
+
+class ProjectBackfillSimilarIssuesEmbeddingsRecordsTest(APITestCase):
+    def setUp(self):
+        super().setUp()
+        self.login_as(self.user)
+        self.org = self.create_organization(owner=self.user)
+        self.project = self.create_project(organization=self.org)
+        self.url = reverse(
+            "sentry-api-0-project-backfill-similar-embeddings-records",
+            kwargs={
+                "organization_slug": self.project.organization.slug,
+                "project_slug": self.project.slug,
+            },
+        )
+
+    def test_post_no_feature_flag(self):
+        response = self.client.post(self.url, data={})
+        assert response.status_code == 404, response.content
+
+    @patch(
+        "sentry.api.endpoints.project_backfill_similar_issues_embeddings_records.is_active_superuser",
+        return_value=False,
+    )
+    def test_post_not_superuser(self, mock_is_active_superuser):
+        response = self.client.post(self.url, data={})
+        assert response.status_code == 404, response.content
+
+    @patch(
+        "sentry.api.endpoints.project_backfill_similar_issues_embeddings_records.is_active_superuser",
+        return_value=True,
+    )
+    @patch(
+        "sentry.api.endpoints.project_backfill_similar_issues_embeddings_records.backfill_seer_grouping_records.delay"
+    )
+    @with_feature("projects:similarity-embeddings-backfill")
+    def test_post_success_no_last_processed_id(
+        self, mock_backfill_seer_grouping_records, mock_is_active_superuser
+    ):
+        response = self.client.post(self.url, data={})
+        assert response.status_code == 204, response.content
+        mock_backfill_seer_grouping_records.assert_called_with(self.project.id, None)
+
+    @patch(
+        "sentry.api.endpoints.project_backfill_similar_issues_embeddings_records.is_active_superuser",
+        return_value=True,
+    )
+    @patch(
+        "sentry.api.endpoints.project_backfill_similar_issues_embeddings_records.backfill_seer_grouping_records.delay"
+    )
+    @with_feature("projects:similarity-embeddings-backfill")
+    def test_post_success_last_processed_id(
+        self, mock_backfill_seer_grouping_records, mock_is_active_superuser
+    ):
+        response = self.client.post(self.url, data={"last_processed_id": "8"})
+        assert response.status_code == 204, response.content
+        mock_backfill_seer_grouping_records.assert_called_with(self.project.id, 8)