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

1"""Module for the invite user use case.""" 

2from dataclasses import dataclass 

3 

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) 

22 

23 

24@dataclass(kw_only=True, frozen=True, slots=True) 

25class InviteUserCommand: 

26 """Input for the use case. 

27 

28 [InviteUser][kwai.modules.identity.invite_user.InviteUser] 

29 

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 """ 

37 

38 first_name: str 

39 last_name: str 

40 email: str 

41 expiration_in_days: int = 7 

42 remark: str = "" 

43 

44 

45class InviteUser: 

46 """Invite user use case. 

47 

48 This use case will try to create an invitation for a user. 

49 """ 

50 

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. 

59 

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 

70 

71 async def execute(self, command: InviteUserCommand) -> UserInvitationEntity: 

72 """Execute the use case. 

73 

74 Args: 

75 command: The input for this use case. 

76 

77 Returns: 

78 A user invitation 

79 

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 

90 

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 ) 

100 

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 ) 

112 

113 await self._bus.publish(UserInvitationCreatedEvent(uuid=str(invitation.uuid))) 

114 

115 return invitation