diff --git a/src/sentry/deletions/defaults/sentry_app.py b/src/sentry/deletions/defaults/sentry_app.py index 66570ad9458c3d..cae5c23e43c6ac 100644 --- a/src/sentry/deletions/defaults/sentry_app.py +++ b/src/sentry/deletions/defaults/sentry_app.py @@ -1,7 +1,10 @@ from collections.abc import Sequence +from sentry.constants import ObjectStatus from sentry.deletions.base import BaseRelation, ModelDeletionTask, ModelRelation from sentry.sentry_apps.models.sentry_app import SentryApp +from sentry.types.region import find_all_region_names +from sentry.workflow_engine.service.action import action_service class SentryAppDeletionTask(ModelDeletionTask[SentryApp]): @@ -25,6 +28,11 @@ def mark_deletion_in_progress(self, instance_list: Sequence[SentryApp]) -> None: instance.update(status=SentryAppStatus.DELETION_IN_PROGRESS) def delete_instance(self, instance: SentryApp) -> None: - # action service RPC goes here, iterating by region because children are deleted first + for region_name in find_all_region_names(): + action_service.update_action_status_for_sentry_app_via_sentry_app_id( + region_name=region_name, + status=ObjectStatus.DISABLED, + sentry_app_id=instance.id, + ) return super().delete_instance(instance) diff --git a/src/sentry/deletions/defaults/sentry_app_installation.py b/src/sentry/deletions/defaults/sentry_app_installation.py index 494d317ae5b860..b61bb888e9c051 100644 --- a/src/sentry/deletions/defaults/sentry_app_installation.py +++ b/src/sentry/deletions/defaults/sentry_app_installation.py @@ -1,8 +1,10 @@ from collections.abc import Sequence +from sentry.constants import ObjectStatus from sentry.deletions.base import BaseRelation, ModelDeletionTask, ModelRelation from sentry.deletions.defaults.apigrant import ModelApiGrantDeletionTask from sentry.sentry_apps.models.sentry_app_installation import SentryAppInstallation +from sentry.workflow_engine.service.action import action_service class SentryAppInstallationDeletionTask(ModelDeletionTask[SentryAppInstallation]): @@ -27,6 +29,10 @@ def mark_deletion_in_progress(self, instance_list: Sequence[SentryAppInstallatio pass def delete_instance(self, instance: SentryAppInstallation) -> None: - # action_service RPC goes here + action_service.update_action_status_for_sentry_app_via_uuid( + organization_id=instance.organization_id, + status=ObjectStatus.DISABLED, + sentry_app_install_uuid=instance.uuid, + ) return super().delete_instance(instance) diff --git a/tests/sentry/deletions/test_sentry_app.py b/tests/sentry/deletions/test_sentry_app.py index 151c1b723150a2..39269f9aa066fd 100644 --- a/tests/sentry/deletions/test_sentry_app.py +++ b/tests/sentry/deletions/test_sentry_app.py @@ -2,15 +2,19 @@ from django.db import connections, router from sentry import deletions +from sentry.constants import ObjectStatus from sentry.models.apiapplication import ApiApplication +from sentry.notifications.models.notificationaction import ActionTarget from sentry.sentry_apps.models.sentry_app import SentryApp from sentry.sentry_apps.models.sentry_app_installation import SentryAppInstallation from sentry.testutils.cases import TestCase -from sentry.testutils.silo import control_silo_test +from sentry.testutils.silo import control_silo_test, create_test_regions from sentry.users.models.user import User +from sentry.workflow_engine.models import Action +from sentry.workflow_engine.typings.notification_action import SentryAppIdentifier -@control_silo_test +@control_silo_test(regions=create_test_regions("us", "de")) class TestSentryAppDeletionTask(TestCase): def setUp(self) -> None: self.user = self.create_user() @@ -54,3 +58,28 @@ def test_soft_deletes_sentry_app(self) -> None: ) assert c.fetchone()[0] == 1 + + def test_disables_actions(self) -> None: + action = self.create_action( + type=Action.Type.SENTRY_APP, + config={ + "target_identifier": str(self.sentry_app.id), + "sentry_app_identifier": SentryAppIdentifier.SENTRY_APP_ID, + "target_type": ActionTarget.SENTRY_APP, + }, + ) + other_action = self.create_action( + type=Action.Type.SENTRY_APP, + config={ + "target_identifier": "1212121212", + "sentry_app_identifier": SentryAppIdentifier.SENTRY_APP_ID, + "target_type": ActionTarget.SENTRY_APP, + }, + ) + deletions.exec_sync(self.sentry_app) + + action.refresh_from_db() + assert action.status == ObjectStatus.DISABLED + + other_action.refresh_from_db() + assert other_action.status == ObjectStatus.ACTIVE diff --git a/tests/sentry/deletions/test_sentry_app_installations.py b/tests/sentry/deletions/test_sentry_app_installations.py index 9c0ae715e5720c..db78466beea6e6 100644 --- a/tests/sentry/deletions/test_sentry_app_installations.py +++ b/tests/sentry/deletions/test_sentry_app_installations.py @@ -3,9 +3,11 @@ from django.db.transaction import get_connection from sentry import deletions +from sentry.constants import ObjectStatus from sentry.deletions.tasks.hybrid_cloud import schedule_hybrid_cloud_foreign_key_jobs from sentry.models.apigrant import ApiGrant from sentry.models.apitoken import ApiToken +from sentry.notifications.models.notificationaction import ActionTarget from sentry.sentry_apps.installations import SentryAppInstallationCreator from sentry.sentry_apps.models.sentry_app_installation import SentryAppInstallation from sentry.sentry_apps.models.sentry_app_installation_for_provider import ( @@ -17,6 +19,8 @@ from sentry.testutils.cases import TestCase from sentry.testutils.outbox import outbox_runner from sentry.testutils.silo import assume_test_silo_mode, control_silo_test +from sentry.workflow_engine.models import Action +from sentry.workflow_engine.typings.notification_action import SentryAppIdentifier @control_silo_test @@ -106,3 +110,28 @@ def test_soft_deletes_installation(self) -> None: ) assert c.fetchone()[0] == 1 + + def test_disables_actions(self) -> None: + action = self.create_action( + type=Action.Type.SENTRY_APP, + config={ + "target_identifier": self.install.uuid, + "sentry_app_identifier": SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID, + "target_type": ActionTarget.SENTRY_APP, + }, + ) + other_action = self.create_action( + type=Action.Type.SENTRY_APP, + config={ + "target_identifier": "1234567890", + "sentry_app_identifier": SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID, + "target_type": ActionTarget.SENTRY_APP, + }, + ) + deletions.exec_sync(self.install) + + action.refresh_from_db() + assert action.status == ObjectStatus.DISABLED + + other_action.refresh_from_db() + assert other_action.status == ObjectStatus.ACTIVE