Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/sentry/receivers/outbox/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,11 @@ def process_sentry_app_installation_deletes(
payload: Mapping[str, Any],
**kwds: Any,
):
action_service.update_action_status_for_sentry_app_via_uuid__region(
action_service.update_action_status_for_sentry_app_installation__region(
region_name=region_name,
status=ObjectStatus.DISABLED,
sentry_app_install_uuid=payload.get("uuid"),
sentry_app_id=payload.get("sentry_app_id"),
organization_id=payload.get("organization_id"),
sentry_app_id=payload["sentry_app_id"],
organization_id=payload["organization_id"],
)


Expand Down
1 change: 0 additions & 1 deletion src/sentry/sentry_apps/models/sentry_app_installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ def outboxes_for_delete(self) -> list[ControlOutbox]:
category=OutboxCategory.SENTRY_APP_INSTALLATION_DELETE,
region_name=region_name,
payload={
"uuid": self.uuid,
"sentry_app_id": self.sentry_app_id,
"organization_id": self.organization_id,
},
Expand Down
82 changes: 17 additions & 65 deletions src/sentry/workflow_engine/service/action/impl.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import logging

from sentry.workflow_engine.models import Action
from sentry.workflow_engine.service.action.service import ActionService
from sentry.workflow_engine.typings.notification_action import SentryAppIdentifier

logger = logging.getLogger(__name__)


class DatabaseBackedActionService(ActionService):
def delete_actions_for_organization_integration(
Expand All @@ -32,78 +28,34 @@ def update_action_status_for_organization_integration(
dataconditiongroupaction__condition_group__organization_id=organization_id,
).update(status=status)

def update_action_status_for_sentry_app_via_uuid(
def update_action_status_for_sentry_app_installation(
self,
*,
organization_id: int,
status: int,
sentry_app_install_uuid: str | None = None,
sentry_app_id: int | None = None,
sentry_app_id: int,
) -> None:
sentry_app_id_actions = Action.objects.none()
installation_uuid_actions = Action.objects.none()

if sentry_app_id:
sentry_app_id_actions = Action.objects.filter(
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_ID,
config__target_identifier=str(sentry_app_id),
type=Action.Type.SENTRY_APP,
dataconditiongroupaction__condition_group__organization_id=organization_id,
)
if sentry_app_install_uuid:
installation_uuid_actions = Action.objects.filter(
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID,
config__target_identifier=sentry_app_install_uuid,
type=Action.Type.SENTRY_APP,
dataconditiongroupaction__condition_group__organization_id=organization_id,
)

actions = sentry_app_id_actions | installation_uuid_actions
if actions:
actions.update(status=status)
Action.objects.filter(
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_ID,
config__target_identifier=str(sentry_app_id),
type=Action.Type.SENTRY_APP,
dataconditiongroupaction__condition_group__organization_id=organization_id,
).update(status=status)

def update_action_status_for_sentry_app_via_uuid__region(
def update_action_status_for_sentry_app_installation__region(
self,
*,
region_name: str,
status: int,
sentry_app_install_uuid: str | None = None,
organization_id: int | None = None,
sentry_app_id: int | None = None,
organization_id: int,
sentry_app_id: int,
) -> None:
sentry_app_id_actions = Action.objects.none()
installation_uuid_actions = Action.objects.none()

if not sentry_app_id and not organization_id:
logger.info(
"Expected sentry_app_id and organization_id, but they were not passed",
extra={"sentry_app_id": sentry_app_id, "organization_id": organization_id},
)

if sentry_app_id and organization_id:
sentry_app_id_actions = Action.objects.filter(
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_ID,
config__target_identifier=str(sentry_app_id),
type=Action.Type.SENTRY_APP,
dataconditiongroupaction__condition_group__organization_id=organization_id,
)
if sentry_app_install_uuid and organization_id:
installation_uuid_actions = Action.objects.filter(
config__target_identifier=sentry_app_install_uuid,
type=Action.Type.SENTRY_APP,
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID,
dataconditiongroupaction__condition_group__organization_id=organization_id,
)
elif sentry_app_install_uuid:
installation_uuid_actions = Action.objects.filter(
config__target_identifier=sentry_app_install_uuid,
type=Action.Type.SENTRY_APP,
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID,
)

actions = sentry_app_id_actions | installation_uuid_actions
if actions:
actions.update(status=status)
Action.objects.filter(
config__sentry_app_identifier=SentryAppIdentifier.SENTRY_APP_ID,
config__target_identifier=str(sentry_app_id),
type=Action.Type.SENTRY_APP,
dataconditiongroupaction__condition_group__organization_id=organization_id,
).update(status=status)

def update_action_status_for_sentry_app_via_sentry_app_id(
self,
Expand Down
12 changes: 5 additions & 7 deletions src/sentry/workflow_engine/service/action/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,26 +36,24 @@ def update_action_status_for_organization_integration(

@regional_rpc_method(resolve=ByOrganizationId())
@abc.abstractmethod
def update_action_status_for_sentry_app_via_uuid(
def update_action_status_for_sentry_app_installation(
self,
*,
organization_id: int,
status: int,
sentry_app_install_uuid: str | None = None,
Comment thread
cursor[bot] marked this conversation as resolved.
sentry_app_id: int | None = None,
sentry_app_id: int,
) -> None:
pass

@regional_rpc_method(resolve=ByRegionName())
@abc.abstractmethod
def update_action_status_for_sentry_app_via_uuid__region(
def update_action_status_for_sentry_app_installation__region(
self,
*,
region_name: str,
status: int,
sentry_app_install_uuid: str | None = None,
organization_id: int | None = None,
sentry_app_id: int | None = None,
organization_id: int,
sentry_app_id: int,
Comment on lines +55 to +56
Copy link
Copy Markdown
Member

@GabeVillalobos GabeVillalobos Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be a new RPC method versus an in-place replacement. Additionally, we probably want to phase this out in 3 parts:

  1. Merge the new update_action_status_for_sentry_app_installation__region definition
  2. Update the outbox receiver to use the new method after 1 has deployed
  3. Remove the old, unused call.

Reason being, when this deploys to DE or US as-is, control will continue to send old update_action_status_for_sentry_app_via_uuid__region method calls which are no longer available in region, causing a bunch of failures, and dropped action cleanups a backlog of outboxes while this continues to fail.

) -> None:
pass

Expand Down
125 changes: 2 additions & 123 deletions tests/sentry/workflow_engine/service/test_action_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,67 +250,6 @@ def test_enable_actions_for_organization_integration_mixed_types(self) -> None:
assert action is not None
assert action.status == ObjectStatus.DISABLED

def test_update_action_status_for_sentry_app__installation_uuid(self) -> None:
sentry_app_installation = self.create_sentry_app_installation(
slug=self.sentry_app.slug,
organization=self.organization,
)
installation_uuid_action = self.create_action(
type=Action.Type.SENTRY_APP,
config={
"target_identifier": sentry_app_installation.uuid,
"sentry_app_identifier": SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID,
"target_type": ActionTarget.SENTRY_APP,
},
)
dcg = self.create_data_condition_group()
self.create_data_condition_group_action(
action=installation_uuid_action, condition_group=dcg
)

sentry_app_id_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,
},
)
dcg2 = self.create_data_condition_group()
self.create_data_condition_group_action(action=sentry_app_id_action, condition_group=dcg2)

# make a new org to ensure we are not disabling actions related to the same sentry app based on a different installation being removed
org2 = self.create_organization()
self.create_sentry_app_installation(
slug=self.sentry_app.slug,
organization=org2,
)
sentry_app_id_action2 = 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,
},
)
dcg3 = self.create_data_condition_group(organization=org2)
self.create_data_condition_group_action(action=sentry_app_id_action2, condition_group=dcg3)

action_service.update_action_status_for_sentry_app_via_uuid(
organization_id=self.organization.id,
sentry_app_install_uuid=sentry_app_installation.uuid,
sentry_app_id=sentry_app_installation.sentry_app.id,
status=ObjectStatus.DISABLED,
)

with assume_test_silo_mode(SiloMode.REGION):
installation_uuid_action.refresh_from_db()
sentry_app_id_action.refresh_from_db()
sentry_app_id_action2.refresh_from_db()
assert installation_uuid_action.status == ObjectStatus.DISABLED
assert sentry_app_id_action.status == ObjectStatus.DISABLED
assert sentry_app_id_action2.status == ObjectStatus.ACTIVE

def test_update_action_status_for_sentry_app__sentry_app_id(self) -> None:
sentry_app_installation = self.create_sentry_app_installation(
slug=self.sentry_app.slug,
Expand Down Expand Up @@ -344,7 +283,7 @@ def test_update_action_status_for_sentry_app__sentry_app_id(self) -> None:
dcg = self.create_data_condition_group(organization=org2)
self.create_data_condition_group_action(action=sentry_app_id_action2, condition_group=dcg)

action_service.update_action_status_for_sentry_app_via_uuid(
action_service.update_action_status_for_sentry_app_installation(
organization_id=self.organization.id,
sentry_app_id=sentry_app_installation.sentry_app.id,
status=ObjectStatus.DISABLED,
Expand All @@ -356,66 +295,6 @@ def test_update_action_status_for_sentry_app__sentry_app_id(self) -> None:
assert sentry_app_id_action.status == ObjectStatus.DISABLED
assert sentry_app_id_action2.status == ObjectStatus.ACTIVE

def test_update_action_status_for_sentry_app__installation_uuid__region(self) -> None:
sentry_app_installation = self.create_sentry_app_installation(
slug=self.sentry_app.slug,
organization=self.organization,
)
installation_uuid_action = self.create_action(
type=Action.Type.SENTRY_APP,
config={
"target_identifier": sentry_app_installation.uuid,
"sentry_app_identifier": SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID,
"target_type": ActionTarget.SENTRY_APP,
},
)
dcg = self.create_data_condition_group()
self.create_data_condition_group_action(
action=installation_uuid_action, condition_group=dcg
)
sentry_app_id_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,
},
)
dcg2 = self.create_data_condition_group()
self.create_data_condition_group_action(action=sentry_app_id_action, condition_group=dcg2)

# make a new org to ensure we are not disabling actions related to the same sentry app
org2 = self.create_organization()
self.create_sentry_app_installation(
slug=self.sentry_app.slug,
organization=org2,
)
sentry_app_id_action2 = 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,
},
)
dcg3 = self.create_data_condition_group(organization=org2)
self.create_data_condition_group_action(action=sentry_app_id_action2, condition_group=dcg3)

action_service.update_action_status_for_sentry_app_via_uuid__region(
region_name="us",
sentry_app_install_uuid=sentry_app_installation.uuid,
sentry_app_id=sentry_app_installation.sentry_app.id,
organization_id=self.organization.id,
status=ObjectStatus.DISABLED,
)
with assume_test_silo_mode(SiloMode.REGION):
installation_uuid_action.refresh_from_db()
sentry_app_id_action.refresh_from_db()
sentry_app_id_action2.refresh_from_db()
assert installation_uuid_action.status == ObjectStatus.DISABLED
assert sentry_app_id_action.status == ObjectStatus.DISABLED
assert sentry_app_id_action2.status == ObjectStatus.ACTIVE

def test_update_action_status_for_sentry_app__sentry_app_id__region(self) -> None:
sentry_app_installation = self.create_sentry_app_installation(
slug=self.sentry_app.slug,
Expand Down Expand Up @@ -449,7 +328,7 @@ def test_update_action_status_for_sentry_app__sentry_app_id__region(self) -> Non
dcg = self.create_data_condition_group(organization=org2)
self.create_data_condition_group_action(action=sentry_app_id_action2, condition_group=dcg)

action_service.update_action_status_for_sentry_app_via_uuid__region(
action_service.update_action_status_for_sentry_app_installation__region(
region_name="us",
sentry_app_id=sentry_app_installation.sentry_app.id,
organization_id=self.organization.id,
Expand Down
Loading