diff --git a/src/sentry/workflow_engine/endpoints/project_detector_details.py b/src/sentry/workflow_engine/endpoints/project_detector_details.py index d692aba489db22..586f0343f00da7 100644 --- a/src/sentry/workflow_engine/endpoints/project_detector_details.py +++ b/src/sentry/workflow_engine/endpoints/project_detector_details.py @@ -11,12 +11,20 @@ from sentry.apidocs.constants import ( RESPONSE_BAD_REQUEST, RESPONSE_FORBIDDEN, + RESPONSE_NO_CONTENT, RESPONSE_NOT_FOUND, RESPONSE_UNAUTHORIZED, ) from sentry.apidocs.parameters import DetectorParams, GlobalParams +from sentry.deletions.models.scheduleddeletion import RegionScheduledDeletion +from sentry.models.project import Project from sentry.workflow_engine.endpoints.serializers import DetectorSerializer -from sentry.workflow_engine.models import Detector +from sentry.workflow_engine.models import ( + DataConditionGroup, + DataSource, + DataSourceDetector, + Detector, +) @region_silo_endpoint @@ -57,7 +65,7 @@ def convert_args(self, request: Request, detector_id, *args, **kwargs): 404: RESPONSE_NOT_FOUND, }, ) - def get(self, request: Request, project, detector): + def get(self, request: Request, project: Project, detector: Detector): """ Fetch a detector ````````````````````````` @@ -69,3 +77,51 @@ def get(self, request: Request, project, detector): DetectorSerializer(), ) return Response(serialized_detector) + + @extend_schema( + operation_id="Delete a Detector", + parameters=[ + GlobalParams.ORG_ID_OR_SLUG, + GlobalParams.PROJECT_ID_OR_SLUG, + DetectorParams.DETECTOR_ID, + ], + responses={ + 204: RESPONSE_NO_CONTENT, + 403: RESPONSE_FORBIDDEN, + 404: RESPONSE_NOT_FOUND, + }, + ) + def delete(self, request: Request, project: Project, detector: Detector): + """ + Delete a detector + """ + try: + data_condition_group = DataConditionGroup.objects.get( + id=detector.workflow_condition_group.id + ) + except DataConditionGroup.DoesNotExist: + pass + + if data_condition_group: + RegionScheduledDeletion.schedule(data_condition_group, days=0, actor=request.user) + + try: + data_source_detector = DataSourceDetector.objects.get(detector_id=detector.id) + except DataSourceDetector.DoesNotExist: + pass + + if data_source_detector: + RegionScheduledDeletion.schedule(data_source_detector, days=0, actor=request.user) + + try: + data_sources = DataSource.objects.filter(detector=detector.id) + except DataSource.DoesNotExist: + pass + + if data_sources: + for data_source in data_sources: + RegionScheduledDeletion.schedule(data_source, days=0, actor=request.user) + + RegionScheduledDeletion.schedule(detector, days=0, actor=request.user) + # TODO add audit log entry + return Response(status=204) diff --git a/src/sentry/workflow_engine/endpoints/urls.py b/src/sentry/workflow_engine/endpoints/urls.py index 15dcf661f601df..bd6e21d66a65ef 100644 --- a/src/sentry/workflow_engine/endpoints/urls.py +++ b/src/sentry/workflow_engine/endpoints/urls.py @@ -10,7 +10,6 @@ # - GET /detector w/ filters # - GET /detector/:id # - PUT /detector/:id -# - DELETE /detector/:id # Remaining Workflows Endpoints # - GET /workflow w/ filters diff --git a/tests/sentry/workflow_engine/endpoints/test_project_detector_details.py b/tests/sentry/workflow_engine/endpoints/test_project_detector_details.py index 92ddf261433f22..59c23e4c9c39ad 100644 --- a/tests/sentry/workflow_engine/endpoints/test_project_detector_details.py +++ b/tests/sentry/workflow_engine/endpoints/test_project_detector_details.py @@ -1,7 +1,10 @@ from sentry.api.serializers import serialize +from sentry.deletions.models.scheduleddeletion import RegionScheduledDeletion from sentry.incidents.grouptype import MetricAlertFire from sentry.testutils.cases import APITestCase +from sentry.testutils.outbox import outbox_runner from sentry.testutils.silo import region_silo_test +from sentry.workflow_engine.models import DataConditionGroup, DataSource, DataSourceDetector class ProjectDetectorDetailsBaseTest(APITestCase): @@ -10,16 +13,56 @@ class ProjectDetectorDetailsBaseTest(APITestCase): def setUp(self): super().setUp() self.login_as(user=self.user) + self.data_condition_group = self.create_data_condition_group() + self.detector = self.create_detector( + project_id=self.project.id, + name="Test Detector", + type=MetricAlertFire.slug, + workflow_condition_group=self.data_condition_group, + ) + data_source = DataSource.objects.filter( + id__in=[data_source.id for data_source in self.detector.data_sources.all()] + ).first() + self.create_data_source_detector(data_source=data_source, detector=self.detector) @region_silo_test class ProjectDetectorIndexGetTest(ProjectDetectorDetailsBaseTest): def test_simple(self): - detector = self.create_detector( - project_id=self.project.id, name="Test Detector", type=MetricAlertFire.slug + response = self.get_success_response( + self.organization.slug, self.project.slug, self.detector.id ) - response = self.get_success_response(self.organization.slug, self.project.slug, detector.id) - assert response.data == serialize(detector) + assert response.data == serialize(self.detector) def test_does_not_exist(self): self.get_error_response(self.organization.slug, self.project.slug, 3, status_code=404) + + +@region_silo_test +class ProjectDetectorIndexDeleteTest(ProjectDetectorDetailsBaseTest): + method = "DELETE" + + def test_simple(self): + detector_id = self.detector.id + data_condition_group = DataConditionGroup.objects.get( + id=self.detector.workflow_condition_group.id + ) + data_source_detector = DataSourceDetector.objects.get(detector_id=detector_id) + data_source = DataSource.objects.get(detector=detector_id) + + with outbox_runner(): + self.get_success_response(self.organization.slug, self.project.slug, self.detector.id) + + self.detector.refresh_from_db() + assert RegionScheduledDeletion.objects.filter( + model_name="Detector", object_id=detector_id + ).exists() + assert RegionScheduledDeletion.objects.filter( + model_name="DataConditionGroup", object_id=data_condition_group.id + ).exists() + assert RegionScheduledDeletion.objects.filter( + model_name="DataSourceDetector", object_id=data_source_detector.id + ).exists() + assert RegionScheduledDeletion.objects.filter( + model_name="DataSource", object_id=data_source.id + ).exists()