diff --git a/src/sentry/api/endpoints/avatar/user.py b/src/sentry/api/endpoints/avatar/user.py index 6179cc0da08084..04d61788a61ef2 100644 --- a/src/sentry/api/endpoints/avatar/user.py +++ b/src/sentry/api/endpoints/avatar/user.py @@ -7,7 +7,7 @@ from sentry.api.base import control_silo_endpoint from sentry.api.bases.avatar import AvatarMixin from sentry.api.bases.user import UserEndpoint -from sentry.models.avatars.user_avatar import UserAvatar +from sentry.users.models.user_avatar import UserAvatar from sentry.users.services.user.serial import serialize_rpc_user from sentry.users.services.user.service import user_service diff --git a/src/sentry/api/serializers/models/user.py b/src/sentry/api/serializers/models/user.py index 5c076ce791c956..089bf4f0ab5457 100644 --- a/src/sentry/api/serializers/models/user.py +++ b/src/sentry/api/serializers/models/user.py @@ -18,13 +18,13 @@ from sentry.auth.elevated_mode import has_elevated_mode from sentry.hybridcloud.services.organization_mapping import organization_mapping_service from sentry.models.authidentity import AuthIdentity -from sentry.models.avatars.user_avatar import UserAvatar from sentry.models.organization import OrganizationStatus from sentry.models.organizationmapping import OrganizationMapping from sentry.models.organizationmembermapping import OrganizationMemberMapping from sentry.organizations.services.organization import RpcOrganizationSummary from sentry.users.models.authenticator import Authenticator from sentry.users.models.user import User +from sentry.users.models.user_avatar import UserAvatar from sentry.users.models.user_option import UserOption from sentry.users.models.useremail import UserEmail from sentry.users.models.userpermission import UserPermission diff --git a/src/sentry/models/avatars/__init__.py b/src/sentry/models/avatars/__init__.py index 80a8e66fcc7a7d..0365753d58435f 100644 --- a/src/sentry/models/avatars/__init__.py +++ b/src/sentry/models/avatars/__init__.py @@ -2,12 +2,10 @@ from .control_base import ControlAvatarBase from .organization_avatar import OrganizationAvatar from .sentry_app_avatar import SentryAppAvatar -from .user_avatar import UserAvatar __all__ = ( "AvatarBase", "ControlAvatarBase", "OrganizationAvatar", "SentryAppAvatar", - "UserAvatar", ) diff --git a/src/sentry/notifications/utils/avatar.py b/src/sentry/notifications/utils/avatar.py index 0908bb377771be..6b2a3b80f9481e 100644 --- a/src/sentry/notifications/utils/avatar.py +++ b/src/sentry/notifications/utils/avatar.py @@ -4,8 +4,8 @@ from django.utils.html import format_html from django.utils.safestring import SafeString -from sentry.models.avatars.user_avatar import UserAvatar from sentry.users.models.user import User +from sentry.users.models.user_avatar import UserAvatar from sentry.users.services.user import RpcUser from sentry.utils.assets import get_asset_url from sentry.utils.avatar import get_email_avatar diff --git a/src/sentry/testutils/factories.py b/src/sentry/testutils/factories.py index 101d238532b0e6..4e5cff702ea91d 100644 --- a/src/sentry/testutils/factories.py +++ b/src/sentry/testutils/factories.py @@ -83,7 +83,6 @@ from sentry.models.authidentity import AuthIdentity from sentry.models.authprovider import AuthProvider from sentry.models.avatars.sentry_app_avatar import SentryAppAvatar -from sentry.models.avatars.user_avatar import UserAvatar from sentry.models.commit import Commit from sentry.models.commitauthor import CommitAuthor from sentry.models.commitfilechange import CommitFileChange @@ -164,6 +163,7 @@ UptimeSubscription, ) from sentry.users.models.user import User +from sentry.users.models.user_avatar import UserAvatar from sentry.users.models.user_option import UserOption from sentry.users.models.useremail import UserEmail from sentry.users.models.userpermission import UserPermission diff --git a/src/sentry/users/models/user.py b/src/sentry/users/models/user.py index a530deba0a44c0..96c884f32eeb6a 100644 --- a/src/sentry/users/models/user.py +++ b/src/sentry/users/models/user.py @@ -41,7 +41,6 @@ from sentry.hybridcloud.outbox.category import OutboxCategory from sentry.integrations.types import EXTERNAL_PROVIDERS, ExternalProviders from sentry.locks import locks -from sentry.models.avatars import UserAvatar from sentry.models.lostpasswordhash import LostPasswordHash from sentry.models.organizationmapping import OrganizationMapping from sentry.models.organizationmembermapping import OrganizationMemberMapping @@ -49,6 +48,7 @@ from sentry.organizations.services.organization import RpcRegionUser, organization_service from sentry.types.region import find_all_region_names, find_regions_for_user from sentry.users.models.authenticator import Authenticator +from sentry.users.models.user_avatar import UserAvatar from sentry.users.models.useremail import UserEmail from sentry.users.services.user import RpcUser from sentry.utils.http import absolute_uri @@ -337,10 +337,10 @@ def merge_to(from_user: User, to_user: User) -> None: # TODO: we could discover relations automatically and make this useful from sentry.models.auditlogentry import AuditLogEntry from sentry.models.authidentity import AuthIdentity - from sentry.models.avatars.user_avatar import UserAvatar from sentry.models.identity import Identity from sentry.models.organizationmembermapping import OrganizationMemberMapping from sentry.users.models.authenticator import Authenticator + from sentry.users.models.user_avatar import UserAvatar from sentry.users.models.user_option import UserOption from sentry.users.models.useremail import UserEmail diff --git a/src/sentry/models/avatars/user_avatar.py b/src/sentry/users/models/user_avatar.py similarity index 93% rename from src/sentry/models/avatars/user_avatar.py rename to src/sentry/users/models/user_avatar.py index df7d44047368a9..db2e86e4faf621 100644 --- a/src/sentry/models/avatars/user_avatar.py +++ b/src/sentry/users/models/user_avatar.py @@ -1,6 +1,7 @@ from __future__ import annotations import contextlib +from collections.abc import Generator from enum import IntEnum from typing import Any, ClassVar, Self @@ -10,17 +11,16 @@ from sentry.db.models.manager.base import BaseManager from sentry.hybridcloud.models.outbox import ControlOutboxBase from sentry.hybridcloud.outbox.category import OutboxCategory +from sentry.models.avatars import ControlAvatarBase from sentry.types.region import find_regions_for_user -from . import ControlAvatarBase - class UserAvatarType(IntEnum): LETTER_AVATAR = 0 UPLOAD = 1 GRAVATAR = 2 - def api_name(self): + def api_name(self) -> str: return self.name.lower() @classmethod @@ -63,7 +63,7 @@ def outboxes_for_update(self, shard_identifier: int | None = None) -> list[Contr ) @contextlib.contextmanager - def _maybe_prepare_outboxes(self, *, outbox_before_super: bool): + def _maybe_prepare_outboxes(self, *, outbox_before_super: bool) -> Generator[None]: from sentry.hybridcloud.models.outbox import outbox_context with outbox_context( @@ -88,5 +88,5 @@ def delete(self, *args: Any, **kwds: Any) -> tuple[int, dict[str, Any]]: with self._maybe_prepare_outboxes(outbox_before_super=True): return super().delete(*args, **kwds) - def get_cache_key(self, size): + def get_cache_key(self, size: int) -> str: return f"avatar:{self.user_id}:{size}" diff --git a/src/sentry/users/services/user/impl.py b/src/sentry/users/services/user/impl.py index 2c03a22491f26c..227aa343cea815 100644 --- a/src/sentry/users/services/user/impl.py +++ b/src/sentry/users/services/user/impl.py @@ -21,12 +21,12 @@ from sentry.hybridcloud.services.organization_mapping.model import RpcOrganizationMapping from sentry.hybridcloud.services.organization_mapping.serial import serialize_organization_mapping from sentry.models.authidentity import AuthIdentity -from sentry.models.avatars import UserAvatar from sentry.models.organization import OrganizationStatus from sentry.models.organizationmapping import OrganizationMapping from sentry.models.organizationmembermapping import OrganizationMemberMapping from sentry.signals import user_signup from sentry.users.models.user import User +from sentry.users.models.user_avatar import UserAvatar from sentry.users.models.useremail import UserEmail from sentry.users.services.user import ( RpcAvatar, diff --git a/src/sentry/users/services/user/serial.py b/src/sentry/users/services/user/serial.py index 4599b4a1957efb..bedfeef5db89c6 100644 --- a/src/sentry/users/services/user/serial.py +++ b/src/sentry/users/services/user/serial.py @@ -6,8 +6,8 @@ from django.utils.functional import LazyObject from sentry.db.models.manager.base_query_set import BaseQuerySet -from sentry.models.avatars.user_avatar import UserAvatar from sentry.users.models.user import User +from sentry.users.models.user_avatar import UserAvatar from sentry.users.services.user import ( RpcAuthenticator, RpcAvatar, diff --git a/src/sentry/web/frontend/user_avatar.py b/src/sentry/web/frontend/user_avatar.py index af27b9bcf4949f..24875a13d071c6 100644 --- a/src/sentry/web/frontend/user_avatar.py +++ b/src/sentry/web/frontend/user_avatar.py @@ -1,4 +1,4 @@ -from sentry.models.avatars.user_avatar import UserAvatar +from sentry.users.models.user_avatar import UserAvatar from sentry.web.frontend.base import AvatarPhotoView, control_silo_view diff --git a/tests/sentry/api/endpoints/test_user_avatar.py b/tests/sentry/api/endpoints/test_user_avatar.py index 430dafbd437a15..60d505a42544d3 100644 --- a/tests/sentry/api/endpoints/test_user_avatar.py +++ b/tests/sentry/api/endpoints/test_user_avatar.py @@ -4,11 +4,11 @@ from django.urls import reverse from sentry import options as options_store -from sentry.models.avatars.user_avatar import UserAvatar, UserAvatarType from sentry.models.files import ControlFile, File from sentry.silo.base import SiloMode from sentry.testutils.cases import APITestCase from sentry.testutils.silo import assume_test_silo_mode, control_silo_test +from sentry.users.models.user_avatar import UserAvatar, UserAvatarType @control_silo_test diff --git a/tests/sentry/api/serializers/test_user.py b/tests/sentry/api/serializers/test_user.py index ec10cd9392245b..ae20acbe6c681d 100644 --- a/tests/sentry/api/serializers/test_user.py +++ b/tests/sentry/api/serializers/test_user.py @@ -3,10 +3,10 @@ from sentry.auth.authenticators import available_authenticators from sentry.models.authidentity import AuthIdentity from sentry.models.authprovider import AuthProvider -from sentry.models.avatars.user_avatar import UserAvatar from sentry.testutils.cases import TestCase from sentry.testutils.silo import control_silo_test from sentry.users.models.authenticator import Authenticator +from sentry.users.models.user_avatar import UserAvatar from sentry.users.models.useremail import UserEmail from sentry.users.models.userpermission import UserPermission diff --git a/tests/sentry/models/test_organization_avatar.py b/tests/sentry/models/test_organization_avatar.py new file mode 100644 index 00000000000000..851d2833f99e98 --- /dev/null +++ b/tests/sentry/models/test_organization_avatar.py @@ -0,0 +1,17 @@ +from sentry.models.avatars.organization_avatar import OrganizationAvatar +from sentry.models.files.file import File +from sentry.testutils.cases import TestCase + + +class OrganizationAvatarTestCase(TestCase): + def test_set_null(self): + org = self.create_organization() + afile = File.objects.create(name="avatar.png", type=OrganizationAvatar.FILE_TYPE) + avatar = OrganizationAvatar.objects.create(organization=org, file_id=afile.id) + + assert avatar.get_file() == afile + + afile.delete() + assert avatar.get_file() is None + assert OrganizationAvatar.objects.get(id=avatar.id).file_id is None + assert OrganizationAvatar.objects.get(id=avatar.id).get_file() is None diff --git a/tests/sentry/models/test_projectownership.py b/tests/sentry/models/test_projectownership.py index c59cb305d11058..12d2251bbe12cf 100644 --- a/tests/sentry/models/test_projectownership.py +++ b/tests/sentry/models/test_projectownership.py @@ -1,6 +1,5 @@ from unittest.mock import patch -from sentry.models.avatars.user_avatar import UserAvatar from sentry.models.groupassignee import GroupAssignee from sentry.models.groupowner import GroupOwner, GroupOwnerType, OwnerRuleType from sentry.models.projectownership import ProjectOwnership @@ -11,6 +10,7 @@ from sentry.testutils.silo import assume_test_silo_mode_of from sentry.testutils.skips import requires_snuba from sentry.types.actor import Actor, ActorType +from sentry.users.models.user_avatar import UserAvatar from sentry.users.services.user.service import user_service pytestmark = requires_snuba diff --git a/tests/sentry/models/test_avatar.py b/tests/sentry/users/models/test_user_avatar.py similarity index 58% rename from tests/sentry/models/test_avatar.py rename to tests/sentry/users/models/test_user_avatar.py index 8aa21174eb8616..64f0b795b77cf3 100644 --- a/tests/sentry/models/test_avatar.py +++ b/tests/sentry/users/models/test_user_avatar.py @@ -1,10 +1,8 @@ from sentry import options as options_store -from sentry.models.avatars.organization_avatar import OrganizationAvatar -from sentry.models.avatars.user_avatar import UserAvatar from sentry.models.files.control_file import ControlFile -from sentry.models.files.file import File from sentry.testutils.cases import TestCase from sentry.testutils.silo import control_silo_test +from sentry.users.models.user_avatar import UserAvatar @control_silo_test @@ -26,17 +24,3 @@ def test_set_null(self): assert avatar.get_file() is None assert UserAvatar.objects.get(id=avatar.id).control_file_id is None assert UserAvatar.objects.get(id=avatar.id).get_file() is None - - -class OrganizationAvatarTestCase(TestCase): - def test_set_null(self): - org = self.create_organization() - afile = File.objects.create(name="avatar.png", type=OrganizationAvatar.FILE_TYPE) - avatar = OrganizationAvatar.objects.create(organization=org, file_id=afile.id) - - assert avatar.get_file() == afile - - afile.delete() - assert avatar.get_file() is None - assert OrganizationAvatar.objects.get(id=avatar.id).file_id is None - assert OrganizationAvatar.objects.get(id=avatar.id).get_file() is None diff --git a/tests/sentry/web/frontend/test_user_avatar.py b/tests/sentry/web/frontend/test_user_avatar.py index 01d247c48191f1..e577dcecf684ac 100644 --- a/tests/sentry/web/frontend/test_user_avatar.py +++ b/tests/sentry/web/frontend/test_user_avatar.py @@ -2,10 +2,10 @@ from django.urls import reverse -from sentry.models.avatars.user_avatar import UserAvatar from sentry.models.files.control_file import ControlFile from sentry.testutils.cases import TestCase from sentry.testutils.silo import control_silo_test +from sentry.users.models.user_avatar import UserAvatar from sentry.web.frontend.generic import FOREVER_CACHE