-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy path0550_migrate_no_action_dupe_issue_alerts.py
102 lines (81 loc) · 3.65 KB
/
0550_migrate_no_action_dupe_issue_alerts.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# Generated by Django 3.2.20 on 2023-09-12 18:15
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from django.db import migrations
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps
from sentry.new_migrations.migrations import CheckedMigration
from sentry.utils.query import RangeQuerySetWrapperWithProgressBar
if TYPE_CHECKING:
from sentry.models.project import Project
class ObjectStatus:
ACTIVE = 0
HIDDEN = 1
PENDING_DELETION = 2
DELETION_IN_PROGRESS = 3
DISABLED = 1
def has_duplicate_rule(
apps: StateApps, rule_data: dict[str, Any], project: Project, rule_id: int
) -> bool:
Rule = apps.get_model("sentry", "Rule")
matchers = {key for key in list(rule_data.keys()) if key not in ("name", "user_id")}
extra_fields = ["actions", "environment"]
matchers.update(extra_fields)
existing_rules = Rule.objects.exclude(id=rule_id).filter(
project=project, status=ObjectStatus.ACTIVE
)
for existing_rule in existing_rules:
keys = 0
matches = 0
for matcher in matchers:
if existing_rule.data.get(matcher) and rule_data.get(matcher):
keys += 1
if existing_rule.data[matcher] == rule_data[matcher]:
matches += 1
elif matcher in extra_fields:
if not existing_rule.data.get(matcher) and not rule_data.get(matcher):
# neither rule has the matcher
continue
elif matcher == "environment":
if existing_rule.environment_id and rule_data.get(matcher):
keys += 1
if existing_rule.environment_id == rule_data.get(matcher):
matches += 1
else:
keys += 1
else:
# one rule has the matcher and the other one doesn't
keys += 1
if keys == matches:
return True
return False
def migrate_bad_rules(apps: StateApps, schema_editor: BaseDatabaseSchemaEditor) -> None:
Rule = apps.get_model("sentry", "Rule")
for rule in RangeQuerySetWrapperWithProgressBar(Rule.objects.all()):
if not rule.data.get("actions", []) or has_duplicate_rule(
apps, rule.data, rule.project, rule.id
):
rule.status = ObjectStatus.DISABLED
rule.save(update_fields=["status"])
class Migration(CheckedMigration):
# This flag is used to mark that a migration shouldn't be automatically run in production. For
# the most part, this should only be used for operations where it's safe to run the migration
# after your code has deployed. So this should not be used for most operations that alter the
# schema of a table.
# Here are some things that make sense to mark as post deployment:
# - Large data migrations. Typically we want these to be run manually by ops so that they can
# be monitored and not block the deploy for a long period of time while they run.
# - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
# have ops run this and not block the deploy. Note that while adding an index is a schema
# change, it's completely safe to run the operation after the code has deployed.
is_post_deployment = True
dependencies = [
("sentry", "0549_re_add_groupsubscription_columns"),
]
operations = [
migrations.RunPython(
migrate_bad_rules,
migrations.RunPython.noop,
hints={"tables": ["sentry_rule"]},
),
]