Coverage for kwai/modules/identity/invite_user.py: 100%
38 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-09-05 17:55 +0000
« prev ^ index » next coverage.py v7.3.0, created at 2023-09-05 17:55 +0000
1"""Module for the invite user use case."""
2from dataclasses import dataclass
4from kwai.core.domain.exceptions import UnprocessableException
5from kwai.core.domain.value_objects.email_address import EmailAddress
6from kwai.core.domain.value_objects.local_timestamp import LocalTimestamp
7from kwai.core.domain.value_objects.name import Name
8from kwai.core.domain.value_objects.unique_id import UniqueId
9from kwai.core.events.bus import Bus
10from kwai.modules.identity.user_invitations.user_invitation import UserInvitationEntity
11from kwai.modules.identity.user_invitations.user_invitation_events import (
12 UserInvitationCreatedEvent,
13)
14from kwai.modules.identity.user_invitations.user_invitation_repository import (
15 UserInvitationRepository,
16)
17from kwai.modules.identity.users.user import UserEntity
18from kwai.modules.identity.users.user_repository import (
19 UserNotFoundException,
20 UserRepository,
21)
24@dataclass(kw_only=True, frozen=True, slots=True)
25class InviteUserCommand:
26 """Input for the use case.
28 [InviteUser][kwai.modules.identity.invite_user.InviteUser]
30 Attributes:
31 first_name: Firstname of the invited
32 last_name: Lastname of the invited
33 email: Email address of the invited
34 expiration_in_days: After how many days will the invitation expire?
35 remark: A remark about this invitation
36 """
38 first_name: str
39 last_name: str
40 email: str
41 expiration_in_days: int = 7
42 remark: str = ""
45class InviteUser:
46 """Invite user use case.
48 This use case will try to create an invitation for a user.
49 """
51 def __init__(
52 self,
53 user: UserEntity,
54 user_repo: UserRepository,
55 user_invitation_repo: UserInvitationRepository,
56 bus: Bus,
57 ):
58 """Initialize the use case.
60 Args:
61 user: The inviter.
62 user_repo: The repository to check if the email address is still free.
63 user_invitation_repo: The repository for creating an invitation.
64 bus: A message bus for publishing the user invitation created event.
65 """
66 self._user = user
67 self._user_repo = user_repo
68 self._user_invitation_repo = user_invitation_repo
69 self._bus = bus
71 async def execute(self, command: InviteUserCommand) -> UserInvitationEntity:
72 """Execute the use case.
74 Args:
75 command: The input for this use case.
77 Returns:
78 A user invitation
80 Raises:
81 UnprocessableException: raised when the email address is already in use or
82 when there are still pending invitations for the email address.
83 """
84 email_address = EmailAddress(command.email)
85 try:
86 await self._user_repo.get_user_by_email(email_address)
87 raise UnprocessableException(f"{command.email} is already used.")
88 except UserNotFoundException:
89 pass
91 query = (
92 self._user_invitation_repo.create_query()
93 .filter_by_email(email_address)
94 .filter_active(LocalTimestamp.create_now())
95 )
96 if await query.count() > 0:
97 raise UnprocessableException(
98 f"There are still pending invitations for {command.email}"
99 )
101 invitation = await self._user_invitation_repo.create(
102 UserInvitationEntity(
103 email=email_address,
104 name=Name(first_name=command.first_name, last_name=command.last_name),
105 uuid=UniqueId.generate(),
106 expired_at=LocalTimestamp.create_with_delta(
107 days=command.expiration_in_days
108 ),
109 user=self._user,
110 )
111 )
113 await self._bus.publish(UserInvitationCreatedEvent(uuid=str(invitation.uuid)))
115 return invitation