Skip to content

Commit e701623

Browse files
ref(control_silo): Move Email model to Users module (#75915)
Move email model to the users module and leave shim for getsentry. ref(#73856) --------- Co-authored-by: Mark Story <[email protected]>
1 parent 954360a commit e701623

File tree

9 files changed

+68
-64
lines changed

9 files changed

+68
-64
lines changed

src/sentry/backup/imports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ def _import(
102102
"""
103103

104104
# Import here to prevent circular module resolutions.
105-
from sentry.models.email import Email
106105
from sentry.models.organization import Organization
107106
from sentry.models.organizationmember import OrganizationMember
108107
from sentry.models.user import User
108+
from sentry.users.models.email import Email
109109

110110
if SiloMode.get_current_mode() == SiloMode.CONTROL:
111111
errText = "Imports must be run in REGION or MONOLITH instances only"

src/sentry/models/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from sentry.users.models.email import * # NOQA
2+
13
from .activity import * # NOQA
24
from .apiapplication import * # NOQA
35
from .apiauthorization import * # NOQA
@@ -29,7 +31,6 @@
2931
from .deploy import * # NOQA
3032
from .distribution import * # NOQA
3133
from .dynamicsampling import * # NOQA
32-
from .email import * # NOQA
3334
from .environment import * # NOQA
3435
from .event import * # NOQA
3536
from .eventattachment import * # NOQA

src/sentry/models/email.py

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,3 @@
1-
from __future__ import annotations
1+
from sentry.users.models.email import Email
22

3-
from django.db import models
4-
from django.forms import model_to_dict
5-
from django.utils import timezone
6-
from django.utils.translation import gettext_lazy as _
7-
8-
from sentry.backup.dependencies import ImportKind, PrimaryKeyMap, get_model_name
9-
from sentry.backup.helpers import ImportFlags
10-
from sentry.backup.scopes import ImportScope, RelocationScope
11-
from sentry.db.models import CIEmailField, Model, control_silo_model, sane_repr
12-
13-
14-
@control_silo_model
15-
class Email(Model):
16-
"""
17-
Email represents a unique email. Email settings (unsubscribe state) should be associated here.
18-
UserEmail represents whether a given user account has access to that email.
19-
"""
20-
21-
__relocation_scope__ = RelocationScope.User
22-
__relocation_dependencies__ = {"sentry.User"}
23-
__relocation_custom_ordinal__ = ["email"]
24-
25-
email = CIEmailField(_("email address"), unique=True, max_length=75)
26-
date_added = models.DateTimeField(default=timezone.now)
27-
28-
class Meta:
29-
app_label = "sentry"
30-
db_table = "sentry_email"
31-
32-
__repr__ = sane_repr("email")
33-
34-
@classmethod
35-
def query_for_relocation_export(cls, q: models.Q, pk_map: PrimaryKeyMap) -> models.Q:
36-
from sentry.models.user import User
37-
from sentry.models.useremail import UserEmail
38-
39-
# `Sentry.Email` models don't have any explicit dependencies on `Sentry.User`, so we need to
40-
# find them manually via `UserEmail`.
41-
emails = UserEmail.objects.filter(
42-
user_id__in=pk_map.get_pks(get_model_name(User))
43-
).values_list("email", flat=True)
44-
45-
return q & models.Q(email__in=emails)
46-
47-
def write_relocation_import(
48-
self, _s: ImportScope, _f: ImportFlags
49-
) -> tuple[int, ImportKind] | None:
50-
# Ensure that we never attempt to duplicate email entries, as they must always be unique.
51-
(email, created) = self.__class__.objects.get_or_create(
52-
email=self.email, defaults=model_to_dict(self)
53-
)
54-
if email:
55-
self.pk = email.pk
56-
self.save()
57-
58-
return (self.pk, ImportKind.Inserted if created else ImportKind.Existing)
3+
__all__ = ("Email",)

src/sentry/receivers/email.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from django.db import IntegrityError, router, transaction
22
from django.db.models.signals import post_delete, post_save
33

4-
from sentry.models.email import Email
54
from sentry.models.useremail import UserEmail
5+
from sentry.users.models.email import Email
66

77

88
def create_email(instance, created, **kwargs):

src/sentry/users/models/email.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from __future__ import annotations
2+
3+
from django.db import models
4+
from django.forms import model_to_dict
5+
from django.utils import timezone
6+
from django.utils.translation import gettext_lazy as _
7+
8+
from sentry.backup.dependencies import ImportKind, PrimaryKeyMap, get_model_name
9+
from sentry.backup.helpers import ImportFlags
10+
from sentry.backup.scopes import ImportScope, RelocationScope
11+
from sentry.db.models import CIEmailField, Model, control_silo_model, sane_repr
12+
13+
14+
@control_silo_model
15+
class Email(Model):
16+
"""
17+
Email represents a unique email. Email settings (unsubscribe state) should be associated here.
18+
UserEmail represents whether a given user account has access to that email.
19+
"""
20+
21+
__relocation_scope__ = RelocationScope.User
22+
__relocation_dependencies__ = {"sentry.User"}
23+
__relocation_custom_ordinal__ = ["email"]
24+
25+
email = CIEmailField(_("email address"), unique=True, max_length=75)
26+
date_added = models.DateTimeField(default=timezone.now)
27+
28+
class Meta:
29+
app_label = "sentry"
30+
db_table = "sentry_email"
31+
32+
__repr__ = sane_repr("email")
33+
34+
@classmethod
35+
def query_for_relocation_export(cls, q: models.Q, pk_map: PrimaryKeyMap) -> models.Q:
36+
from sentry.models.user import User
37+
from sentry.models.useremail import UserEmail
38+
39+
# `Sentry.Email` models don't have any explicit dependencies on `Sentry.User`, so we need to
40+
# find them manually via `UserEmail`.
41+
emails = UserEmail.objects.filter(
42+
user_id__in=pk_map.get_pks(get_model_name(User))
43+
).values_list("email", flat=True)
44+
45+
return q & models.Q(email__in=emails)
46+
47+
def write_relocation_import(
48+
self, _s: ImportScope, _f: ImportFlags
49+
) -> tuple[int, ImportKind] | None:
50+
# Ensure that we never attempt to duplicate email entries, as they must always be unique.
51+
(email, created) = self.__class__.objects.get_or_create(
52+
email=self.email, defaults=model_to_dict(self)
53+
)
54+
if email:
55+
self.pk = email.pk
56+
self.save()
57+
58+
return (self.pk, ImportKind.Inserted if created else ImportKind.Existing)

tests/sentry/backup/test_exports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from sentry.backup.scopes import ExportScope
1111
from sentry.backup.validate import validate
1212
from sentry.db import models
13-
from sentry.models.email import Email
1413
from sentry.models.options.option import Option
1514
from sentry.models.organization import Organization
1615
from sentry.models.organizationmember import OrganizationMember
@@ -26,6 +25,7 @@
2625
export_to_file,
2726
)
2827
from sentry.testutils.helpers.datetime import freeze_time
28+
from sentry.users.models.email import Email
2929
from tests.sentry.backup import get_matching_exportable_models
3030

3131

tests/sentry/backup/test_findings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
RpcImportError,
2020
RpcImportErrorKind,
2121
)
22-
from sentry.models.email import Email
2322
from sentry.testutils.cases import TestCase
23+
from sentry.users.models.email import Email
2424

2525
encoder = FindingJSONEncoder(
2626
sort_keys=True,

tests/sentry/backup/test_imports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
from sentry.backup.services.import_export.model import RpcImportErrorKind
3636
from sentry.models.apitoken import DEFAULT_EXPIRATION, ApiToken, generate_token
3737
from sentry.models.authenticator import Authenticator
38-
from sentry.models.email import Email
3938
from sentry.models.importchunk import (
4039
ControlImportChunk,
4140
ControlImportChunkReplica,
@@ -81,6 +80,7 @@
8180
)
8281
from sentry.testutils.hybrid_cloud import use_split_dbs
8382
from sentry.testutils.silo import assume_test_silo_mode
83+
from sentry.users.models.email import Email
8484
from tests.sentry.backup import (
8585
expect_models,
8686
get_matching_exportable_models,

tests/sentry/runner/commands/test_backup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
from sentry.backup.findings import InstanceID
2626
from sentry.backup.imports import ImportingError
2727
from sentry.backup.services.import_export.model import RpcImportError, RpcImportErrorKind
28-
from sentry.models.email import Email
2928
from sentry.runner.commands.backup import backup, export, import_
3029
from sentry.silo.base import SiloMode
3130
from sentry.testutils.cases import TestCase, TransactionTestCase
@@ -36,6 +35,7 @@
3635
generate_rsa_key_pair,
3736
)
3837
from sentry.testutils.silo import assume_test_silo_mode
38+
from sentry.users.models.email import Email
3939
from sentry.utils import json
4040

4141
GOOD_FILE_PATH = get_fixture_path("backup", "fresh-install.json")

0 commit comments

Comments
 (0)