Browse Source

fix(migrations): Fix NotificationSettings (#24869)

Marcos Gaeta 4 years ago
parent
commit
f35e949d3d

+ 1 - 1
migrations_lockfile.txt

@@ -10,7 +10,7 @@ auth: 0008_alter_user_username_max_length
 contenttypes: 0002_remove_content_type_name
 jira_ac: 0001_initial
 nodestore: 0002_nodestore_no_dictfield
-sentry: 0183_make_codemapping_unique_on_projectcodeowners
+sentry: 0184_copy_useroptions_to_notificationsettings_2
 sessions: 0001_initial
 sites: 0002_alter_domain_unique
 social_auth: 0001_initial

+ 106 - 0
src/sentry/migrations/0184_copy_useroptions_to_notificationsettings_2.py

@@ -0,0 +1,106 @@
+# Generated by Django 1.11.29 on 2021-03-31 18:24
+
+from django.db import migrations
+from enum import Enum
+
+from sentry.utils.query import RangeQuerySetWrapperWithProgressBar
+
+
+class ExternalProviders(Enum):
+    GITHUB = 0
+    GITLAB = 1
+    EMAIL = 100
+    SLACK = 110
+
+
+class NotificationScopeType(Enum):
+    USER = 0
+    ORGANIZATION = 10
+    PROJECT = 20
+
+
+class NotificationSettingTypes(Enum):
+    # top level config of on/off
+    # for workflow also includes SUBSCRIBE_ONLY
+    # for deploy also includes COMMITTED_ONLY
+    DEFAULT = 0
+    # send deploy notifications
+    DEPLOY = 10
+    # notifications for issues
+    ISSUE_ALERTS = 20
+    # notifications for changes in assignment, resolution, comments
+    WORKFLOW = 30
+
+
+class NotificationSettingOptionValues(Enum):
+    DEFAULT = 0  # Defer to a setting one level up.
+    NEVER = 10
+    ALWAYS = 20
+    SUBSCRIBE_ONLY = 30  # workflow
+    COMMITTED_ONLY = 40  # deploy
+
+
+def get_value(user_option):
+    try:
+        int_value = int(user_option.value)
+    except (ValueError, TypeError):
+        return None
+
+    if int_value == 1:
+        return NotificationSettingOptionValues.ALWAYS.value
+    elif int_value == 0:
+        return NotificationSettingOptionValues.NEVER.value
+    return None
+
+
+def copy_useroption_to_notificationsetting(apps, schema_editor):
+    UserOption = apps.get_model("sentry", "UserOption")
+    User = apps.get_model("sentry", "User")
+    NotificationSetting = apps.get_model("sentry", "NotificationSetting")
+    for user_option in RangeQuerySetWrapperWithProgressBar(UserOption.objects.all()):
+        if user_option.key == "subscribe_by_default":  # top level issue alerts on/off
+            value = get_value(user_option)
+            if value is None:
+                continue
+
+            user = User.objects.get(id=user_option.user_id)
+            NotificationSetting.objects.update_or_create(
+                scope_type=NotificationScopeType.USER.value,
+                scope_identifier=user_option.user.id,
+                target_id=user.actor_id,
+                provider=ExternalProviders.EMAIL.value,  # 100
+                type=NotificationSettingTypes.ISSUE_ALERTS.value,
+                defaults={"value": value},  # NotificationSettingOptionValues
+            )
+
+
+class Migration(migrations.Migration):
+    # This flag is used to mark that a migration shouldn't be automatically run in
+    # production. We set this to True for operations that we think are risky and want
+    # someone from ops to run manually and monitor.
+    # General advice is that if in doubt, mark your migration as `is_dangerous`.
+    # Some things you should always mark as dangerous:
+    # - Large data migrations. Typically we want these to be run manually by ops so that
+    #   they can be monitored. Since data migrations will now hold a transaction open
+    #   this is even more important.
+    # - Adding columns to highly active tables, even ones that are NULL.
+    is_dangerous = True
+
+    # This flag is used to decide whether to run this migration in a transaction or not.
+    # By default we prefer to run in a transaction, but for migrations where you want
+    # to `CREATE INDEX CONCURRENTLY` this needs to be set to False. Typically you'll
+    # want to create an index concurrently when adding one to an existing table.
+    # You'll also usually want to set this to `False` if you're writing a data
+    # migration, since we don't want the entire migration to run in one long-running
+    # transaction.
+    atomic = False
+
+    dependencies = [
+        ("sentry", "0183_make_codemapping_unique_on_projectcodeowners"),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            copy_useroption_to_notificationsetting, reverse_code=migrations.RunPython.noop
+        )
+    ]