Coverage for src/kwai/api/v1/auth/endpoints/user_invitations.py: 72%
69 statements
« prev ^ index » next coverage.py v7.7.1, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2024-01-01 00:00 +0000
1"""Module that implements invitations endpoints."""
3from typing import Annotated
5from fastapi import APIRouter, Depends, HTTPException, status
6from loguru import logger
8from kwai.api.dependencies import create_database, get_current_user, get_publisher
9from kwai.api.v1.auth.schemas.user_invitation import UserInvitationDocument
10from kwai.core.db.database import Database
11from kwai.core.db.uow import UnitOfWork
12from kwai.core.domain.exceptions import UnprocessableException
13from kwai.core.domain.value_objects.email_address import InvalidEmailException
14from kwai.core.events.publisher import Publisher
15from kwai.core.json_api import Meta, PaginationModel
16from kwai.modules.identity.delete_user_invitation import (
17 DeleteUserInvitation,
18 DeleteUserInvitationCommand,
19)
20from kwai.modules.identity.get_invitations import GetInvitations, GetInvitationsCommand
21from kwai.modules.identity.get_user_invitation import (
22 GetUserInvitation,
23 GetUserInvitationCommand,
24)
25from kwai.modules.identity.invite_user import InviteUser, InviteUserCommand
26from kwai.modules.identity.recreate_user_invitation import (
27 RecreateUserInvitation,
28 RecreateUserInvitationCommand,
29)
30from kwai.modules.identity.user_invitations.user_invitation_db_repository import (
31 UserInvitationDbRepository,
32)
33from kwai.modules.identity.user_invitations.user_invitation_repository import (
34 UserInvitationNotFoundException,
35)
36from kwai.modules.identity.users.user import UserEntity
37from kwai.modules.identity.users.user_db_repository import UserDbRepository
40router = APIRouter()
43@router.post(
44 "/invitations/{uuid}",
45 summary="Recreate a user invitation",
46 status_code=status.HTTP_201_CREATED,
47 responses={
48 201: {"description": "User invitation is created"},
49 401: {"description": "Not authorized."},
50 422: {"description": "User invitation could not be created"},
51 },
52)
53async def recreate_user_invitation(
54 uuid: str,
55 db: Annotated[Database, Depends(create_database)],
56 user: Annotated[UserEntity, Depends(get_current_user)],
57 publisher: Annotated[Publisher, Depends(get_publisher)],
58) -> UserInvitationDocument:
59 """Recreate a user invitation.
61 Use this API for resending a user invitation. The existing invitation
62 will expire.
63 """
64 command = RecreateUserInvitationCommand(uuid=uuid)
65 try:
66 async with UnitOfWork(db):
67 invitation = await RecreateUserInvitation(
68 user, UserDbRepository(db), UserInvitationDbRepository(db), publisher
69 ).execute(command)
70 except UnprocessableException as ex:
71 logger.warning(f"User invitation could not be processed: {ex}")
72 raise HTTPException(
73 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ex)
74 ) from ex
76 return UserInvitationDocument.create(invitation)
79@router.post(
80 "/invitations",
81 summary="Create a user invitation",
82 status_code=status.HTTP_201_CREATED,
83 responses={
84 201: {"description": "User invitation is created"},
85 401: {"description": "Not authorized."},
86 422: {"description": "User invitation could not be created"},
87 },
88)
89async def create_user_invitation(
90 user_invitation_document: UserInvitationDocument,
91 db=Depends(create_database),
92 user: UserEntity = Depends(get_current_user),
93 publisher=Depends(get_publisher),
94) -> UserInvitationDocument:
95 """Create a user invitation.
97 A wrong email address or a still pending user invitation will result in a 422
98 status code.
99 """
100 command = InviteUserCommand(
101 first_name=user_invitation_document.resource.attributes.first_name,
102 last_name=user_invitation_document.resource.attributes.last_name,
103 email=user_invitation_document.resource.attributes.email,
104 remark=user_invitation_document.resource.attributes.remark,
105 )
107 try:
108 async with UnitOfWork(db):
109 invitation = await InviteUser(
110 user, UserDbRepository(db), UserInvitationDbRepository(db), publisher
111 ).execute(command)
112 except InvalidEmailException as exc:
113 raise HTTPException(
114 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
115 detail="Invalid email address",
116 ) from exc
117 except UnprocessableException as ex:
118 logger.warning(f"User invitation could not be processed: {ex}")
119 raise HTTPException(
120 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ex)
121 ) from ex
123 return UserInvitationDocument.create(invitation)
126@router.delete(
127 "/invitations/{uuid}",
128 summary="Delete a user invitation",
129 status_code=status.HTTP_200_OK,
130 responses={
131 200: {"description": "User invitation is deleted."},
132 401: {"description": "Not authorized."},
133 404: {"description": "User invitation does not exist."},
134 422: {"description": "Invalid unique id passed for the user invitation."},
135 },
136)
137async def delete_user_invitation(
138 uuid: str,
139 db=Depends(create_database),
140 user: UserEntity = Depends(get_current_user),
141) -> None:
142 """Delete the user invitation with the given unique id."""
143 command = DeleteUserInvitationCommand(uuid=uuid)
144 try:
145 async with UnitOfWork(db):
146 await DeleteUserInvitation(UserInvitationDbRepository(db)).execute(command)
147 except UserInvitationNotFoundException as ex:
148 raise HTTPException(
149 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
150 ) from ex
151 except ValueError as ex:
152 raise HTTPException(
153 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ex)
154 ) from ex
157@router.get(
158 "/invitations",
159 summary="Get a list of user invitations",
160 responses={
161 200: {"description": "Ok."},
162 401: {"description": "Not authorized."},
163 },
164)
165async def get_user_invitations(
166 pagination: PaginationModel = Depends(PaginationModel),
167 db=Depends(create_database),
168 user: UserEntity = Depends(get_current_user),
169) -> UserInvitationDocument:
170 """Get all user invitations.
172 Use the page[offset] and page[limit] query parameters to get a paginated result.
173 """
174 command = GetInvitationsCommand(offset=pagination.offset, limit=pagination.limit)
175 count, invitation_iterator = await GetInvitations(
176 UserInvitationDbRepository(db)
177 ).execute(command)
179 result = UserInvitationDocument(
180 meta=Meta(count=count, limit=pagination.limit, offset=pagination.offset),
181 data=[],
182 )
184 async for invitation in invitation_iterator:
185 result.merge(UserInvitationDocument.create(invitation))
187 return result
190@router.get(
191 "/invitations/{uuid}",
192 summary="Get a user invitation",
193 responses={200: {"description": "Ok."}, 401: {"description": "Not authorized."}},
194)
195async def get_user_invitation(
196 uuid: str,
197 db=Depends(create_database),
198 user: UserEntity = Depends(get_current_user),
199) -> UserInvitationDocument:
200 """Get the user invitation with the given unique id."""
201 command = GetUserInvitationCommand(uuid=uuid)
202 try:
203 invitation = await GetUserInvitation(UserInvitationDbRepository(db)).execute(
204 command
205 )
206 except UserInvitationNotFoundException as ex:
207 raise HTTPException(
208 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
209 ) from ex
211 return UserInvitationDocument.create(invitation)