diff --git a/src/sentry/api/endpoints/project_rule_details.py b/src/sentry/api/endpoints/project_rule_details.py index 8342fdf6f598a5..487570db00c564 100644 --- a/src/sentry/api/endpoints/project_rule_details.py +++ b/src/sentry/api/endpoints/project_rule_details.py @@ -366,6 +366,7 @@ def delete(self, request: Request, project, rule) -> Response: - Filters: help control noise by triggering an alert only if the issue matches the specified criteria. - Actions: specify what should happen when the trigger conditions are met and the filters match. """ + rule_id = rule.id rule.update(status=ObjectStatus.PENDING_DELETION) RuleActivity.objects.create( rule=rule, user_id=request.user.id, type=RuleActivityType.DELETED.value @@ -375,7 +376,11 @@ def delete(self, request: Request, project, rule) -> Response: if features.has( "organizations:workflow-engine-issue-alert-dual-write", project.organization ): - delete_migrated_issue_alert(rule) + workflow_id = delete_migrated_issue_alert(rule) + logger.info( + "workflow_engine.issue_alert.deleted", + extra={"rule_id": rule_id, "workflow_id": workflow_id}, + ) self.create_audit_entry( request=request, diff --git a/src/sentry/projects/project_rules/creator.py b/src/sentry/projects/project_rules/creator.py index 49b7f0cdc0cd02..b100d10df0cdcc 100644 --- a/src/sentry/projects/project_rules/creator.py +++ b/src/sentry/projects/project_rules/creator.py @@ -1,3 +1,4 @@ +import logging from collections.abc import Sequence from dataclasses import dataclass from typing import Any @@ -11,6 +12,8 @@ from sentry.types.actor import Actor from sentry.workflow_engine.migration_helpers.issue_alert_migration import IssueAlertMigrator +logger = logging.getLogger(__name__) + @dataclass class ProjectRuleCreator: @@ -32,8 +35,14 @@ def run(self) -> Rule: if features.has( "organizations:workflow-engine-issue-alert-dual-write", self.project.organization ): - # TODO(cathy): handle errors from broken actions - IssueAlertMigrator(self.rule, self.request.user.id if self.request else None).run() + # uncaught errors will rollback the transaction + workflow = IssueAlertMigrator( + self.rule, self.request.user.id if self.request else None + ).run() + logger.info( + "workflow_engine.issue_alert.migrated", + extra={"rule_id": self.rule.id, "workflow_id": workflow.id}, + ) return self.rule diff --git a/src/sentry/projects/project_rules/updater.py b/src/sentry/projects/project_rules/updater.py index 9869f601fdacec..c3335875c9e04d 100644 --- a/src/sentry/projects/project_rules/updater.py +++ b/src/sentry/projects/project_rules/updater.py @@ -1,3 +1,4 @@ +import logging from collections.abc import Sequence from attr import dataclass @@ -12,6 +13,8 @@ update_migrated_issue_alert, ) +logger = logging.getLogger(__name__) + @dataclass class ProjectRuleUpdater: @@ -43,8 +46,12 @@ def run(self) -> Rule: if features.has( "organizations:workflow-engine-issue-alert-dual-write", self.project.organization ): - # TODO(cathy): handle errors from broken actions - update_migrated_issue_alert(self.rule) + # uncaught errors will rollback the transaction + workflow = update_migrated_issue_alert(self.rule) + logger.info( + "workflow_engine.issue_alert.updated", + extra={"rule_id": self.rule.id, "workflow_id": workflow.id}, + ) return self.rule def _update_name(self) -> None: diff --git a/src/sentry/workflow_engine/migration_helpers/issue_alert_dual_write.py b/src/sentry/workflow_engine/migration_helpers/issue_alert_dual_write.py index 00d3bb91bafabf..6250776d91e0c3 100644 --- a/src/sentry/workflow_engine/migration_helpers/issue_alert_dual_write.py +++ b/src/sentry/workflow_engine/migration_helpers/issue_alert_dual_write.py @@ -82,7 +82,7 @@ def create_workflow_actions(if_dcg: DataConditionGroup, actions: list[dict[str, DataConditionGroupAction.objects.bulk_create(dcg_actions) -def update_migrated_issue_alert(rule: Rule): +def update_migrated_issue_alert(rule: Rule) -> Workflow: data = rule.data try: @@ -146,6 +146,8 @@ def update_migrated_issue_alert(rule: Rule): workflow.enabled = True workflow.save() + return workflow + def update_dcg( dcg: DataConditionGroup, @@ -175,7 +177,7 @@ def update_dcg( return dcg -def delete_migrated_issue_alert(rule: Rule): +def delete_migrated_issue_alert(rule: Rule) -> int: try: alert_rule_workflow = AlertRuleWorkflow.objects.get(rule=rule) except AlertRuleWorkflow.DoesNotExist: @@ -183,6 +185,7 @@ def delete_migrated_issue_alert(rule: Rule): return workflow: Workflow = alert_rule_workflow.workflow + workflow_id = workflow.id try: # delete all associated IF DCGs and their conditions @@ -210,6 +213,8 @@ def delete_migrated_issue_alert(rule: Rule): workflow.delete() alert_rule_workflow.delete() + return workflow_id + def delete_workflow_actions(if_dcg: DataConditionGroup): dcg_actions = DataConditionGroupAction.objects.filter(condition_group=if_dcg) diff --git a/src/sentry/workflow_engine/migration_helpers/issue_alert_migration.py b/src/sentry/workflow_engine/migration_helpers/issue_alert_migration.py index 10979ff49cae24..81547c649ace8e 100644 --- a/src/sentry/workflow_engine/migration_helpers/issue_alert_migration.py +++ b/src/sentry/workflow_engine/migration_helpers/issue_alert_migration.py @@ -51,7 +51,7 @@ def __init__( self.project = rule.project self.organization = self.project.organization - def run(self) -> None: + def run(self) -> Workflow: error_detector = self._create_detector_lookup() conditions, filters = split_conditions_and_filters(self.data["conditions"]) action_match = self.data.get("action_match") or Rule.DEFAULT_CONDITION_MATCH