Browse Source

ref(serializers): Split up large file (#32085)

Marcos Gaeta 3 years ago
parent
commit
3d2252640b

+ 12 - 12
mypy.ini

@@ -19,20 +19,24 @@ files = src/sentry/analytics/,
         src/sentry/api/serializers/models/external_actor.py,
         src/sentry/api/serializers/models/integration.py,
         src/sentry/api/serializers/models/notification_setting.py,
-        src/sentry/api/serializers/models/organization_member.py,
+        src/sentry/api/serializers/models/organization.py,
+        src/sentry/api/serializers/models/organization_member/,
         src/sentry/api/serializers/models/team.py,
+        src/sentry/api/serializers/models/user.py,
+        src/sentry/api/serializers/types.py,
         src/sentry/api/validators/external_actor.py,
         src/sentry/api/validators/notifications.py,
+        src/sentry/apidocs/,
         src/sentry/constants.py,
         src/sentry/db/models/base.py,
-        src/sentry/db/models/paranoia.py,
-        src/sentry/db/models/query.py,
-        src/sentry/db/models/utils.py,
         src/sentry/db/models/fields/bounded.py,
         src/sentry/db/models/fields/foreignkey.py,
         src/sentry/db/models/fields/onetoone.py,
         src/sentry/db/models/fields/text.py,
         src/sentry/db/models/manager/,
+        src/sentry/db/models/paranoia.py,
+        src/sentry/db/models/query.py,
+        src/sentry/db/models/utils.py,
         src/sentry/digests/,
         src/sentry/features/,
         src/sentry/grouping/result.py,
@@ -52,8 +56,8 @@ files = src/sentry/analytics/,
         src/sentry/mail/notifications.py,
         src/sentry/models/debugfile.py,
         src/sentry/models/groupsubscription.py,
-        src/sentry/models/rulefirehistory.py,
         src/sentry/models/options/,
+        src/sentry/models/rulefirehistory.py,
         src/sentry/notifications/,
         src/sentry/processing/realtime_metrics/,
         src/sentry/ratelimits/,
@@ -63,13 +67,13 @@ files = src/sentry/analytics/,
         src/sentry/search/base.py,
         src/sentry/search/events/builder.py,
         src/sentry/search/events/constants.py,
-        src/sentry/search/snuba/,
         src/sentry/search/events/types.py,
+        src/sentry/search/snuba/,
         src/sentry/sentry_metrics/,
         src/sentry/shared_integrations/,
+        src/sentry/snuba/entity_subscription.py,
         src/sentry/snuba/outcomes.py,
         src/sentry/snuba/query_subscription_consumer.py,
-        src/sentry/snuba/entity_subscription.py,
         src/sentry/spans/,
         src/sentry/tasks/app_store_connect.py,
         src/sentry/tasks/low_priority_symbolication.py,
@@ -91,11 +95,7 @@ files = src/sentry/analytics/,
         tests/sentry/lang/native/test_appconnect.py,
         tests/sentry/processing/realtime_metrics/,
         tests/sentry/tasks/test_low_priority_symbolication.py,
-        tests/sentry/utils/appleconnect/,
-        src/sentry/apidocs/,
-        src/sentry/api/serializers/models/user.py,
-        src/sentry/api/serializers/models/organization.py,
-        src/sentry/api/serializers/types.py
+        tests/sentry/utils/appleconnect/
 
 ; Enable all options used with --strict
 warn_unused_configs=True

+ 0 - 288
src/sentry/api/serializers/models/organization_member.py

@@ -1,288 +0,0 @@
-from collections import defaultdict
-from datetime import datetime
-from typing import Any, List, Mapping, MutableMapping, Optional, Sequence, Set, cast
-
-from typing_extensions import TypedDict
-
-from sentry import roles
-from sentry.api.serializers import Serializer, register, serialize
-from sentry.api.serializers.models.external_actor import ExternalActorResponse
-from sentry.api.serializers.models.user import UserSerializerResponse
-from sentry.models import (
-    ExternalActor,
-    OrganizationMember,
-    OrganizationMemberTeam,
-    Team,
-    TeamStatus,
-    User,
-)
-from sentry.scim.endpoints.constants import SCIM_SCHEMA_USER
-
-
-def get_serialized_users_by_id(users_set: Set[User], user: User) -> Mapping[str, User]:
-    serialized_users = serialize(users_set, user)
-    return {user["id"]: user for user in serialized_users}
-
-
-def get_team_slugs_by_organization_member_id(
-    organization_members: Sequence[OrganizationMember],
-) -> Mapping[int, List[str]]:
-    """@returns a map of member id -> team_slug[]"""
-    organization_member_tuples = list(
-        OrganizationMemberTeam.objects.filter(
-            team__status=TeamStatus.VISIBLE, organizationmember__in=organization_members
-        ).values_list("organizationmember_id", "team_id")
-    )
-    team_ids = {team_id for (_organization_member_id, team_id) in organization_member_tuples}
-    teams = Team.objects.filter(id__in=team_ids)
-    teams_by_id = {team.id: team for team in teams}
-
-    results = defaultdict(list)
-    for member_id, team_id in organization_member_tuples:
-        results[member_id].append(teams_by_id[team_id].slug)
-    return results
-
-
-def get_organization_id(organization_members: Sequence[OrganizationMember]) -> int:
-    """Ensure all organization_members have the same organization ID and then return that ID."""
-    organization_ids = {
-        organization_member.organization_id for organization_member in organization_members
-    }
-    if len(organization_ids) != 1:
-        raise Exception("Cannot determine organization")
-    return int(organization_ids.pop())
-
-
-# have to use alternative TypedDict syntax because of dashes/colons in names
-_OrganizationMemberFlags = TypedDict(
-    "_OrganizationMemberFlags",
-    {"sso:linked": bool, "sso:invalid": bool, "member-limit:restricted": bool},
-)
-
-
-class OrganizationMemberResponseOptional(TypedDict, total=False):
-    externalUsers: List[ExternalActorResponse]
-
-
-class OrganizationMemberResponse(OrganizationMemberResponseOptional):
-    id: str
-    email: str
-    name: str
-    user: UserSerializerResponse
-    role: str  # TODO: literal/enum
-    roleName: str  # TODO: literal/enum
-    pending: bool
-    expired: str
-    flags: _OrganizationMemberFlags
-    dateCreated: datetime
-    inviteStatus: str
-    inviterName: Optional[str]
-
-
-@register(OrganizationMember)
-class OrganizationMemberSerializer(Serializer):  # type: ignore
-    def __init__(self, expand: Optional[Sequence[str]] = None) -> None:
-        self.expand = expand or []
-
-    def get_attrs(
-        self, item_list: Sequence[OrganizationMember], user: User, **kwargs: Any
-    ) -> MutableMapping[OrganizationMember, MutableMapping[str, Any]]:
-        """
-        Fetch all of the associated Users and ExternalActors needed to serialize
-        the organization_members in `item_list`.
-        TODO(dcramer): assert on relations
-        """
-
-        users_set = {
-            organization_member.user
-            for organization_member in item_list
-            if organization_member.user_id
-        }
-        users_by_id = get_serialized_users_by_id(users_set, user)
-        external_users_map = defaultdict(list)
-
-        if "externalUsers" in self.expand:
-            organization_id = get_organization_id(item_list)
-            external_actors = list(
-                ExternalActor.objects.filter(
-                    actor_id__in={user.actor_id for user in users_set},
-                    organization_id=organization_id,
-                )
-            )
-
-            serialized_list = serialize(external_actors, user, key="user")
-            for serialized in serialized_list:
-                external_users_map[serialized["userId"]].append(serialized)
-
-        attrs: MutableMapping[OrganizationMember, MutableMapping[str, Any]] = {}
-        for item in item_list:
-            user = users_by_id.get(str(item.user_id), None)
-            user_id = user["id"] if user else ""
-            external_users = external_users_map.get(user_id, [])
-            attrs[item] = {
-                "user": user,
-                "externalUsers": external_users,
-            }
-        return attrs
-
-    def serialize(
-        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
-    ) -> OrganizationMemberResponse:
-        d: OrganizationMemberResponse = {
-            "id": str(obj.id),
-            "email": obj.get_email(),
-            "name": obj.user.get_display_name() if obj.user else obj.get_email(),
-            "user": attrs["user"],
-            "role": obj.role,
-            "roleName": roles.get(obj.role).name,
-            "pending": obj.is_pending,
-            "expired": obj.token_expired,
-            "flags": {
-                "sso:linked": bool(getattr(obj.flags, "sso:linked")),
-                "sso:invalid": bool(getattr(obj.flags, "sso:invalid")),
-                "member-limit:restricted": bool(getattr(obj.flags, "member-limit:restricted")),
-            },
-            "dateCreated": obj.date_added,
-            "inviteStatus": obj.get_invite_status_name(),
-            "inviterName": obj.inviter.get_display_name() if obj.inviter else None,
-        }
-
-        if "externalUsers" in self.expand:
-            d["externalUsers"] = attrs.get("externalUsers", [])
-
-        return d
-
-
-class OrganizationMemberWithTeamsResponse(OrganizationMemberResponse):
-    teams: List[str]
-
-
-class OrganizationMemberWithTeamsSerializer(OrganizationMemberSerializer):
-    def get_attrs(
-        self, item_list: Sequence[OrganizationMember], user: User, **kwargs: Any
-    ) -> MutableMapping[OrganizationMember, MutableMapping[str, Any]]:
-        attrs = super().get_attrs(item_list, user)
-
-        team_ids_by_organization_member_id = get_team_slugs_by_organization_member_id(item_list)
-        for item in item_list:
-            teams = team_ids_by_organization_member_id.get(item.id, [])
-            try:
-                attrs[item]["teams"] = teams
-            except KeyError:
-                attrs[item] = {"teams": teams}
-
-        return attrs
-
-    def serialize(
-        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
-    ) -> OrganizationMemberWithTeamsResponse:
-        d = cast(OrganizationMemberWithTeamsResponse, super().serialize(obj, attrs, user))
-        d["teams"] = attrs.get("teams", [])
-        return d
-
-
-class OrganizationMemberWithProjectsResponse(OrganizationMemberResponse):
-    projects: List[str]
-
-
-class OrganizationMemberWithProjectsSerializer(OrganizationMemberSerializer):
-    def __init__(self, *args: Any, **kwargs: Any) -> None:
-        self.project_ids = set(kwargs.pop("project_ids", []))
-        super().__init__(*args, **kwargs)
-
-    def get_attrs(
-        self, item_list: Sequence[OrganizationMember], user: User, **kwargs: Any
-    ) -> MutableMapping[OrganizationMember, MutableMapping[str, Any]]:
-        """
-        Note: For this to be efficient, call
-        `.prefetch_related(
-              'teams',
-              'teams__projectteam_set',
-              'teams__projectteam_set__project',
-        )` on your queryset before using this serializer
-        """
-
-        attrs = super().get_attrs(item_list, user)
-        for org_member in item_list:
-            projects = set()
-            for team in org_member.teams.all():
-                # Filter in python here so that we don't break the prefetch
-                if team.status != TeamStatus.VISIBLE:
-                    continue
-
-                for project_team in team.projectteam_set.all():
-                    if (
-                        project_team.project_id in self.project_ids
-                        and project_team.project.status == TeamStatus.VISIBLE
-                    ):
-                        projects.add(project_team.project.slug)
-
-            projects_list = list(projects)
-            projects_list.sort()
-            attrs[org_member]["projects"] = projects_list
-        return attrs
-
-    def serialize(
-        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
-    ) -> OrganizationMemberWithProjectsResponse:
-        d = cast(OrganizationMemberWithProjectsResponse, super().serialize(obj, attrs, user))
-
-        d["projects"] = attrs.get("projects", [])
-        return d
-
-
-class SCIMName(TypedDict):
-    givenName: str
-    familyName: str
-
-
-class SCIMEmail(TypedDict):
-    primary: bool
-    value: str
-    type: str
-
-
-class OrganizationMemberSCIMSerializerOptional(TypedDict, total=False):
-    """Sentry doesn't use this field but is expected by SCIM"""
-
-    active: bool
-
-
-class SCIMMeta(TypedDict):
-    resourceType: str
-
-
-class OrganizationMemberSCIMSerializerResponse(OrganizationMemberSCIMSerializerOptional):
-    """
-    Conforming to the SCIM RFC, this represents a Sentry Org Member
-    as a SCIM user object.
-    """
-
-    schemas: List[str]
-    id: str
-    userName: str
-    name: SCIMName
-    emails: List[SCIMEmail]
-    meta: SCIMMeta
-
-
-class OrganizationMemberSCIMSerializer(Serializer):  # type: ignore
-    def __init__(self, expand: Optional[Sequence[str]] = None) -> None:
-        self.expand = expand or []
-
-    def serialize(
-        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
-    ) -> OrganizationMemberSCIMSerializerResponse:
-
-        result: OrganizationMemberSCIMSerializerResponse = {
-            "schemas": [SCIM_SCHEMA_USER],
-            "id": str(obj.id),
-            "userName": obj.get_email(),
-            "name": {"givenName": "N/A", "familyName": "N/A"},
-            "emails": [{"primary": True, "value": obj.get_email(), "type": "work"}],
-            "meta": {"resourceType": "User"},
-        }
-        if "active" in self.expand:
-            result["active"] = True
-
-        return result

+ 14 - 0
src/sentry/api/serializers/models/organization_member/__init__.py

@@ -0,0 +1,14 @@
+from .base import OrganizationMemberSerializer
+from .expand.projects import OrganizationMemberWithProjectsSerializer
+from .expand.teams import OrganizationMemberWithTeamsSerializer
+from .response import OrganizationMemberSCIMSerializerResponse, SCIMMeta
+from .scim import OrganizationMemberSCIMSerializer
+
+__all__ = (
+    "OrganizationMemberSCIMSerializer",
+    "OrganizationMemberSerializer",
+    "OrganizationMemberWithProjectsSerializer",
+    "OrganizationMemberWithTeamsSerializer",
+    "OrganizationMemberSCIMSerializerResponse",
+    "SCIMMeta",
+)

+ 83 - 0
src/sentry/api/serializers/models/organization_member/base.py

@@ -0,0 +1,83 @@
+from collections import defaultdict
+from typing import Any, Mapping, MutableMapping, Optional, Sequence
+
+from sentry import roles
+from sentry.api.serializers import Serializer, register, serialize
+from sentry.models import ExternalActor, OrganizationMember, User
+
+from .response import OrganizationMemberResponse
+from .utils import get_organization_id, get_serialized_users_by_id
+
+
+@register(OrganizationMember)
+class OrganizationMemberSerializer(Serializer):  # type: ignore
+    def __init__(self, expand: Optional[Sequence[str]] = None) -> None:
+        self.expand = expand or []
+
+    def get_attrs(
+        self, item_list: Sequence[OrganizationMember], user: User, **kwargs: Any
+    ) -> MutableMapping[OrganizationMember, MutableMapping[str, Any]]:
+        """
+        Fetch all of the associated Users and ExternalActors needed to serialize
+        the organization_members in `item_list`.
+        TODO(dcramer): assert on relations
+        """
+
+        users_set = {
+            organization_member.user
+            for organization_member in item_list
+            if organization_member.user_id
+        }
+        users_by_id = get_serialized_users_by_id(users_set, user)
+        external_users_map = defaultdict(list)
+
+        if "externalUsers" in self.expand:
+            organization_id = get_organization_id(item_list)
+            external_actors = list(
+                ExternalActor.objects.filter(
+                    actor_id__in={user.actor_id for user in users_set},
+                    organization_id=organization_id,
+                )
+            )
+
+            serialized_list = serialize(external_actors, user, key="user")
+            for serialized in serialized_list:
+                external_users_map[serialized["userId"]].append(serialized)
+
+        attrs: MutableMapping[OrganizationMember, MutableMapping[str, Any]] = {}
+        for item in item_list:
+            user = users_by_id.get(str(item.user_id), None)
+            user_id = user["id"] if user else ""
+            external_users = external_users_map.get(user_id, [])
+            attrs[item] = {
+                "user": user,
+                "externalUsers": external_users,
+            }
+        return attrs
+
+    def serialize(
+        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
+    ) -> OrganizationMemberResponse:
+        d: OrganizationMemberResponse = {
+            "id": str(obj.id),
+            "email": obj.get_email(),
+            "name": obj.user.get_display_name() if obj.user else obj.get_email(),
+            "user": attrs["user"],
+            "role": obj.role,
+            "roleName": roles.get(obj.role).name,
+            "pending": obj.is_pending,
+            "expired": obj.token_expired,
+            "flags": {
+                "sso:linked": bool(getattr(obj.flags, "sso:linked")),
+                "sso:invalid": bool(getattr(obj.flags, "sso:invalid")),
+                "member-limit:restricted": bool(getattr(obj.flags, "member-limit:restricted")),
+            },
+            "dateCreated": obj.date_added,
+            "inviteStatus": obj.get_invite_status_name(),
+            "inviterName": obj.inviter.get_display_name() if obj.inviter else None,
+        }
+
+        if "externalUsers" in self.expand:
+            d["externalUsers"] = attrs.get("externalUsers", [])
+
+        return d

+ 0 - 0
src/sentry/api/serializers/models/organization_member/expand/__init__.py


+ 52 - 0
src/sentry/api/serializers/models/organization_member/expand/projects.py

@@ -0,0 +1,52 @@
+from typing import Any, Mapping, MutableMapping, Sequence, cast
+
+from sentry.models import OrganizationMember, TeamStatus, User
+
+from ..base import OrganizationMemberSerializer
+from ..response import OrganizationMemberWithProjectsResponse
+
+
+class OrganizationMemberWithProjectsSerializer(OrganizationMemberSerializer):
+    def __init__(self, *args: Any, **kwargs: Any) -> None:
+        self.project_ids = set(kwargs.pop("project_ids", []))
+        super().__init__(*args, **kwargs)
+
+    def get_attrs(
+        self, item_list: Sequence[OrganizationMember], user: User, **kwargs: Any
+    ) -> MutableMapping[OrganizationMember, MutableMapping[str, Any]]:
+        """
+        Note: For this to be efficient, call
+        `.prefetch_related(
+              'teams',
+              'teams__projectteam_set',
+              'teams__projectteam_set__project',
+        )` on your queryset before using this serializer
+        """
+
+        attrs = super().get_attrs(item_list, user)
+        for org_member in item_list:
+            projects = set()
+            for team in org_member.teams.all():
+                # Filter in python here so that we don't break the prefetch
+                if team.status != TeamStatus.VISIBLE:
+                    continue
+
+                for project_team in team.projectteam_set.all():
+                    if (
+                        project_team.project_id in self.project_ids
+                        and project_team.project.status == TeamStatus.VISIBLE
+                    ):
+                        projects.add(project_team.project.slug)
+
+            projects_list = list(projects)
+            projects_list.sort()
+            attrs[org_member]["projects"] = projects_list
+        return attrs
+
+    def serialize(
+        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
+    ) -> OrganizationMemberWithProjectsResponse:
+        d = cast(OrganizationMemberWithProjectsResponse, super().serialize(obj, attrs, user))
+
+        d["projects"] = attrs.get("projects", [])
+        return d

+ 31 - 0
src/sentry/api/serializers/models/organization_member/expand/teams.py

@@ -0,0 +1,31 @@
+from typing import Any, Mapping, MutableMapping, Sequence, cast
+
+from sentry.models import OrganizationMember, User
+
+from ..base import OrganizationMemberSerializer
+from ..response import OrganizationMemberWithTeamsResponse
+from ..utils import get_team_slugs_by_organization_member_id
+
+
+class OrganizationMemberWithTeamsSerializer(OrganizationMemberSerializer):
+    def get_attrs(
+        self, item_list: Sequence[OrganizationMember], user: User, **kwargs: Any
+    ) -> MutableMapping[OrganizationMember, MutableMapping[str, Any]]:
+        attrs = super().get_attrs(item_list, user)
+
+        team_ids_by_organization_member_id = get_team_slugs_by_organization_member_id(item_list)
+        for item in item_list:
+            teams = team_ids_by_organization_member_id.get(item.id, [])
+            try:
+                attrs[item]["teams"] = teams
+            except KeyError:
+                attrs[item] = {"teams": teams}
+
+        return attrs
+
+    def serialize(
+        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
+    ) -> OrganizationMemberWithTeamsResponse:
+        d = cast(OrganizationMemberWithTeamsResponse, super().serialize(obj, attrs, user))
+        d["teams"] = attrs.get("teams", [])
+        return d

+ 76 - 0
src/sentry/api/serializers/models/organization_member/response.py

@@ -0,0 +1,76 @@
+from datetime import datetime
+from typing import List, Optional
+
+from typing_extensions import TypedDict
+
+from sentry.api.serializers.models.external_actor import ExternalActorResponse
+from sentry.api.serializers.models.user import UserSerializerResponse
+
+
+class SCIMName(TypedDict):
+    givenName: str
+    familyName: str
+
+
+class SCIMEmail(TypedDict):
+    primary: bool
+    value: str
+    type: str
+
+
+class SCIMMeta(TypedDict):
+    resourceType: str
+
+
+class OrganizationMemberSCIMSerializerOptional(TypedDict, total=False):
+    """Sentry doesn't use this field but is expected by SCIM"""
+
+    active: bool
+
+
+# We must use alternative TypedDict syntax because of dashes/colons in names.
+_OrganizationMemberFlags = TypedDict(
+    "_OrganizationMemberFlags",
+    {"sso:linked": bool, "sso:invalid": bool, "member-limit:restricted": bool},
+)
+
+
+class OrganizationMemberResponseOptional(TypedDict, total=False):
+    externalUsers: List[ExternalActorResponse]
+
+
+class OrganizationMemberSCIMSerializerResponse(OrganizationMemberSCIMSerializerOptional):
+    """
+    Conforming to the SCIM RFC, this represents a Sentry Org Member
+    as a SCIM user object.
+    """
+
+    schemas: List[str]
+    id: str
+    userName: str
+    name: SCIMName
+    emails: List[SCIMEmail]
+    meta: SCIMMeta
+
+
+class OrganizationMemberResponse(OrganizationMemberResponseOptional):
+    id: str
+    email: str
+    name: str
+    user: UserSerializerResponse
+    role: str  # TODO: literal/enum
+    roleName: str  # TODO: literal/enum
+    pending: bool
+    expired: str
+    flags: _OrganizationMemberFlags
+    dateCreated: datetime
+    inviteStatus: str
+    inviterName: Optional[str]
+
+
+class OrganizationMemberWithTeamsResponse(OrganizationMemberResponse):
+    teams: List[str]
+
+
+class OrganizationMemberWithProjectsResponse(OrganizationMemberResponse):
+    projects: List[str]

+ 29 - 0
src/sentry/api/serializers/models/organization_member/scim.py

@@ -0,0 +1,29 @@
+from typing import Any, Mapping, Optional, Sequence
+
+from sentry.api.serializers import Serializer
+from sentry.models import OrganizationMember
+from sentry.scim.endpoints.constants import SCIM_SCHEMA_USER
+
+from .response import OrganizationMemberSCIMSerializerResponse
+
+
+class OrganizationMemberSCIMSerializer(Serializer):  # type: ignore
+    def __init__(self, expand: Optional[Sequence[str]] = None) -> None:
+        self.expand = expand or []
+
+    def serialize(
+        self, obj: OrganizationMember, attrs: Mapping[str, Any], user: Any, **kwargs: Any
+    ) -> OrganizationMemberSCIMSerializerResponse:
+
+        result: OrganizationMemberSCIMSerializerResponse = {
+            "schemas": [SCIM_SCHEMA_USER],
+            "id": str(obj.id),
+            "userName": obj.get_email(),
+            "name": {"givenName": "N/A", "familyName": "N/A"},
+            "emails": [{"primary": True, "value": obj.get_email(), "type": "work"}],
+            "meta": {"resourceType": "User"},
+        }
+        if "active" in self.expand:
+            result["active"] = True
+
+        return result

+ 39 - 0
src/sentry/api/serializers/models/organization_member/utils.py

@@ -0,0 +1,39 @@
+from collections import defaultdict
+from typing import List, Mapping, Sequence, Set
+
+from sentry.api.serializers import serialize
+from sentry.models import OrganizationMember, OrganizationMemberTeam, Team, TeamStatus, User
+
+
+def get_serialized_users_by_id(users_set: Set[User], user: User) -> Mapping[str, User]:
+    serialized_users = serialize(users_set, user)
+    return {user["id"]: user for user in serialized_users}
+
+
+def get_team_slugs_by_organization_member_id(
+    organization_members: Sequence[OrganizationMember],
+) -> Mapping[int, List[str]]:
+    """@returns a map of member id -> team_slug[]"""
+    organization_member_tuples = list(
+        OrganizationMemberTeam.objects.filter(
+            team__status=TeamStatus.VISIBLE, organizationmember__in=organization_members
+        ).values_list("organizationmember_id", "team_id")
+    )
+    team_ids = {team_id for (_organization_member_id, team_id) in organization_member_tuples}
+    teams = Team.objects.filter(id__in=team_ids)
+    teams_by_id = {team.id: team for team in teams}
+
+    results = defaultdict(list)
+    for member_id, team_id in organization_member_tuples:
+        results[member_id].append(teams_by_id[team_id].slug)
+    return results
+
+
+def get_organization_id(organization_members: Sequence[OrganizationMember]) -> int:
+    """Ensure all organization_members have the same organization ID and then return that ID."""
+    organization_ids = {
+        organization_member.organization_id for organization_member in organization_members
+    }
+    if len(organization_ids) != 1:
+        raise Exception("Cannot determine organization")
+    return int(organization_ids.pop())

Some files were not shown because too many files changed in this diff