mirror of https://github.com/zulip/zulip.git
scheduled-mails: Migrate existing scheduled emails to new templates.
Migrates existing ScheduledEmails for onboarding emails that have either "zerver/emails/followup_day1" or "zerver/emails/followup_day2" as the email template prefix to instead use the new template prefixes "zerver/emails/account_registered" and "zerver/emails/zulip_onboarding_topics".
This commit is contained in:
parent
438bcc1585
commit
5d5a578d6c
|
@ -0,0 +1,95 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-07-11 10:45
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
from django.db.migrations.state import StateApps
|
||||||
|
from django.db.models import F, Func, JSONField, TextField, Value
|
||||||
|
from django.db.models.functions import Cast
|
||||||
|
|
||||||
|
# ScheduledMessage.type for onboarding emails from zerver/models.py
|
||||||
|
WELCOME = 1
|
||||||
|
|
||||||
|
|
||||||
|
def update_for_followup_day_email_templates_rename(
|
||||||
|
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
|
||||||
|
) -> None:
|
||||||
|
ScheduledEmail = apps.get_model("zerver", "ScheduledEmail")
|
||||||
|
|
||||||
|
account_registered_emails = ScheduledEmail.objects.annotate(
|
||||||
|
as_jsonb=Cast("data", JSONField())
|
||||||
|
).filter(type=WELCOME, as_jsonb__template_prefix="zerver/emails/followup_day1")
|
||||||
|
account_registered_emails.update(
|
||||||
|
data=Cast(
|
||||||
|
Func(
|
||||||
|
F("as_jsonb"),
|
||||||
|
Value(["template_prefix"]),
|
||||||
|
Value("zerver/emails/account_registered", JSONField()),
|
||||||
|
function="jsonb_set",
|
||||||
|
),
|
||||||
|
TextField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
onboarding_zulip_topics_emails = ScheduledEmail.objects.annotate(
|
||||||
|
as_jsonb=Cast("data", JSONField())
|
||||||
|
).filter(type=WELCOME, as_jsonb__template_prefix="zerver/emails/followup_day2")
|
||||||
|
onboarding_zulip_topics_emails.update(
|
||||||
|
data=Cast(
|
||||||
|
Func(
|
||||||
|
F("as_jsonb"),
|
||||||
|
Value(["template_prefix"]),
|
||||||
|
Value("zerver/emails/onboarding_zulip_topics", JSONField()),
|
||||||
|
function="jsonb_set",
|
||||||
|
),
|
||||||
|
TextField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def revert_followup_day_email_templates_rename(
|
||||||
|
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
|
||||||
|
) -> None:
|
||||||
|
ScheduledEmail = apps.get_model("zerver", "ScheduledEmail")
|
||||||
|
|
||||||
|
rename_extradata_realmauditlog_extra_data_json = ScheduledEmail.objects.annotate(
|
||||||
|
as_jsonb=Cast("data", JSONField())
|
||||||
|
).filter(type=WELCOME, as_jsonb__template_prefix="zerver/emails/account_registered")
|
||||||
|
rename_extradata_realmauditlog_extra_data_json.update(
|
||||||
|
data=Cast(
|
||||||
|
Func(
|
||||||
|
F("as_jsonb"),
|
||||||
|
Value(["template_prefix"]),
|
||||||
|
Value("zerver/emails/followup_day1", JSONField()),
|
||||||
|
function="jsonb_set",
|
||||||
|
),
|
||||||
|
TextField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
rename_extradata_realmauditlog_extra_data_json = ScheduledEmail.objects.annotate(
|
||||||
|
as_jsonb=Cast("data", JSONField())
|
||||||
|
).filter(type=WELCOME, as_jsonb__template_prefix="zerver/emails/onboarding_zulip_topics")
|
||||||
|
rename_extradata_realmauditlog_extra_data_json.update(
|
||||||
|
data=Cast(
|
||||||
|
Func(
|
||||||
|
F("as_jsonb"),
|
||||||
|
Value(["template_prefix"]),
|
||||||
|
Value("zerver/emails/followup_day2", JSONField()),
|
||||||
|
function="jsonb_set",
|
||||||
|
),
|
||||||
|
TextField(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0467_rename_extradata_realmauditlog_extra_data_json"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
update_for_followup_day_email_templates_rename,
|
||||||
|
reverse_code=revert_followup_day_email_templates_rename,
|
||||||
|
),
|
||||||
|
]
|
|
@ -4,7 +4,9 @@
|
||||||
# You can also read
|
# You can also read
|
||||||
# https://www.caktusgroup.com/blog/2016/02/02/writing-unit-tests-django-migrations/
|
# https://www.caktusgroup.com/blog/2016/02/02/writing-unit-tests-django-migrations/
|
||||||
# to get a tutorial on the framework that inspired this feature.
|
# to get a tutorial on the framework that inspired this feature.
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
import orjson
|
||||||
from django.db.migrations.state import StateApps
|
from django.db.migrations.state import StateApps
|
||||||
|
|
||||||
from zerver.lib.test_classes import MigrationsTestCase
|
from zerver.lib.test_classes import MigrationsTestCase
|
||||||
|
@ -26,56 +28,75 @@ from zerver.lib.test_helpers import use_db_models
|
||||||
# been tested for a migration being merged.
|
# been tested for a migration being merged.
|
||||||
|
|
||||||
|
|
||||||
class RealmPlaygroundURLPrefix(MigrationsTestCase):
|
class ScheduledEmailData(MigrationsTestCase):
|
||||||
migrate_from = "0462_realmplayground_url_template"
|
migrate_from = "0467_rename_extradata_realmauditlog_extra_data_json"
|
||||||
migrate_to = "0463_backfill_realmplayground_url_template"
|
migrate_to = "0468_rename_followup_day_email_templates"
|
||||||
|
|
||||||
@use_db_models
|
@use_db_models
|
||||||
def setUpBeforeMigration(self, apps: StateApps) -> None:
|
def setUpBeforeMigration(self, apps: StateApps) -> None:
|
||||||
iago = self.example_user("iago")
|
iago = self.example_user("iago")
|
||||||
RealmPlayground = apps.get_model("zerver", "RealmPlayground")
|
ScheduledEmail = apps.get_model("zerver", "ScheduledEmail")
|
||||||
|
send_date = datetime(2025, 1, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
|
||||||
|
|
||||||
urls = [
|
templates = [
|
||||||
"http://example.com/",
|
["zerver/emails/followup_day1", "a", True, 10],
|
||||||
"https://example.com/{",
|
["zerver/emails/followup_day2", "b", False, 20],
|
||||||
"https://example.com/{}",
|
["zerver/emails/onboarding_zulip_guide", "c", True, 30],
|
||||||
"https://example.com/{val}",
|
|
||||||
"https://example.com/{code}",
|
|
||||||
]
|
]
|
||||||
self.realm_playground_ids = []
|
|
||||||
|
|
||||||
for index, url in enumerate(urls):
|
for template in templates:
|
||||||
self.realm_playground_ids.append(
|
email_fields = {
|
||||||
RealmPlayground.objects.create(
|
"template_prefix": template[0],
|
||||||
realm=iago.realm,
|
"string_context": template[1],
|
||||||
name=f"Playground {index}",
|
"boolean_context": template[2],
|
||||||
pygments_language="Python",
|
"integer_context": template[3],
|
||||||
url_prefix=url,
|
}
|
||||||
url_template=None,
|
|
||||||
).id
|
email = ScheduledEmail.objects.create(
|
||||||
)
|
type=1,
|
||||||
self.realm_playground_ids.append(
|
|
||||||
RealmPlayground.objects.create(
|
|
||||||
realm=iago.realm,
|
realm=iago.realm,
|
||||||
name="Existing Playground",
|
scheduled_timestamp=send_date,
|
||||||
pygments_language="Python",
|
data=orjson.dumps(email_fields).decode(),
|
||||||
url_prefix="https://example.com",
|
)
|
||||||
url_template="https://example.com/{code}",
|
email.users.add(iago.id)
|
||||||
).id
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_converted_url_templates(self) -> None:
|
def test_updated_email_templates(self) -> None:
|
||||||
RealmPlayground = self.apps.get_model("zerver", "RealmPlayground")
|
ScheduledEmail = self.apps.get_model("zerver", "ScheduledEmail")
|
||||||
|
send_date = datetime(2025, 1, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
|
||||||
|
|
||||||
expected_urls = [
|
old_templates = [
|
||||||
"http://example.com/{code}",
|
"zerver/emails/followup_day1",
|
||||||
"https://example.com/%7B{code}",
|
"zerver/emails/followup_day2",
|
||||||
"https://example.com/%7B%7D{code}",
|
|
||||||
"https://example.com/%7Bval%7D{code}",
|
|
||||||
"https://example.com/%7Bcode%7D{code}",
|
|
||||||
"https://example.com/{code}",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for realm_playground_id, expected_url in zip(self.realm_playground_ids, expected_urls):
|
current_templates = [
|
||||||
realm_playground = RealmPlayground.objects.get(id=realm_playground_id)
|
"zerver/emails/account_registered",
|
||||||
self.assertEqual(realm_playground.url_template, expected_url)
|
"zerver/emails/onboarding_zulip_guide",
|
||||||
|
"zerver/emails/onboarding_zulip_topics",
|
||||||
|
]
|
||||||
|
|
||||||
|
email_data = [
|
||||||
|
["zerver/emails/account_registered", "a", True, 10],
|
||||||
|
["zerver/emails/onboarding_zulip_topics", "b", False, 20],
|
||||||
|
["zerver/emails/onboarding_zulip_guide", "c", True, 30],
|
||||||
|
]
|
||||||
|
|
||||||
|
scheduled_emails = ScheduledEmail.objects.all()
|
||||||
|
self.assert_length(scheduled_emails, 3)
|
||||||
|
|
||||||
|
checked_emails = []
|
||||||
|
for email in scheduled_emails:
|
||||||
|
self.assertEqual(email.type, 1)
|
||||||
|
self.assertEqual(email.scheduled_timestamp, send_date)
|
||||||
|
|
||||||
|
updated_data = orjson.loads(email.data)
|
||||||
|
template_prefix = updated_data["template_prefix"]
|
||||||
|
self.assertFalse(template_prefix in old_templates)
|
||||||
|
for data in email_data:
|
||||||
|
if template_prefix == data[0]:
|
||||||
|
self.assertEqual(updated_data["string_context"], data[1])
|
||||||
|
self.assertEqual(updated_data["boolean_context"], data[2])
|
||||||
|
self.assertEqual(updated_data["integer_context"], data[3])
|
||||||
|
checked_emails.append(template_prefix)
|
||||||
|
|
||||||
|
self.assertEqual(current_templates, sorted(checked_emails))
|
||||||
|
|
Loading…
Reference in New Issue