Coverage for apps/kwai-api/src/kwai_api/v1/teams/api.py: 81%
123 statements
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
1"""Module that defines the teams API."""
3from typing import Annotated
5from fastapi import APIRouter, Depends, HTTPException, Query, status
6from kwai_bc_identity.users.user import UserEntity
7from kwai_bc_teams.create_team import CreateTeam, CreateTeamCommand
8from kwai_bc_teams.create_team_member import CreateTeamMember, CreateTeamMemberCommand
9from kwai_bc_teams.delete_member_from_team import (
10 DeleteMemberFromTeam,
11 DeleteMemberFromTeamCommand,
12)
13from kwai_bc_teams.delete_team import DeleteTeam, DeleteTeamCommand
14from kwai_bc_teams.domain.team import TeamMemberAlreadyExistException
15from kwai_bc_teams.get_members import GetMembers, GetMembersCommand
16from kwai_bc_teams.get_team import GetTeam, GetTeamCommand
17from kwai_bc_teams.get_teams import GetTeams, GetTeamsCommand
18from kwai_bc_teams.repositories.member_db_repository import MemberDbRepository
19from kwai_bc_teams.repositories.member_repository import MemberNotFoundException
20from kwai_bc_teams.repositories.team_db_repository import TeamDbRepository
21from kwai_bc_teams.repositories.team_repository import TeamNotFoundException
22from kwai_bc_teams.update_team import UpdateTeam, UpdateTeamCommand
23from kwai_core.db.database import Database
24from kwai_core.db.uow import UnitOfWork
25from kwai_core.json_api import PaginationModel
26from pydantic import BaseModel, Field
28from kwai_api.dependencies import create_database, get_current_user
29from kwai_api.v1.teams.presenters import (
30 JsonApiMembersPresenter,
31 JsonApiTeamMemberPresenter,
32 JsonApiTeamMembersPresenter,
33 JsonApiTeamPresenter,
34 JsonApiTeamsPresenter,
35)
36from kwai_api.v1.teams.schemas import (
37 DeleteTeamMembersResource,
38 TeamDocument,
39 TeamMemberDocument,
40 TeamMembersDocument,
41 TeamsDocument,
42)
45router = APIRouter(tags=["teams"])
48@router.get("/teams")
49async def get_teams(
50 database: Annotated[Database, Depends(create_database)],
51) -> TeamsDocument:
52 """Get all teams of the club."""
53 presenter = JsonApiTeamsPresenter()
54 command = GetTeamsCommand(offset=0, limit=0)
55 await GetTeams(TeamDbRepository(database), presenter).execute(command)
56 return presenter.get_document()
59class TeamMemberFilterModel(BaseModel):
60 """Define the JSON:API filter for team members."""
62 team: str | None = Field(Query(default=None, alias="filter[team]"))
65@router.get("/teams/members")
66async def get_members(
67 database: Annotated[Database, Depends(create_database)],
68 pagination: Annotated[PaginationModel, Depends(PaginationModel)],
69 team_filter: Annotated[TeamMemberFilterModel, Depends(TeamMemberFilterModel)],
70) -> TeamMembersDocument:
71 """Get all members that can be part of a team."""
72 presenter = JsonApiMembersPresenter()
73 if team_filter.team is not None:
74 if ":" in team_filter.team:
75 operand, team_id = team_filter.team.split(":")
76 if operand not in ("eq", "noteq"):
77 raise HTTPException(
78 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
79 detail="Invalid operand",
80 )
81 if not team_id.isdigit():
82 raise HTTPException(
83 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
84 detail="Invalid team id",
85 )
87 command = GetMembersCommand(
88 team_id=int(team_id),
89 in_team=operand == "eq",
90 offset=pagination.offset,
91 limit=pagination.limit,
92 )
93 else:
94 if team_filter.team.isdigit():
95 command = GetMembersCommand(
96 team_id=int(team_filter.team),
97 offset=pagination.offset,
98 limit=pagination.limit,
99 )
100 else:
101 raise HTTPException(
102 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
103 detail="Invalid team id",
104 )
105 else:
106 command = GetMembersCommand(offset=pagination.offset, limit=pagination.limit)
107 await GetMembers(MemberDbRepository(database), presenter).execute(command)
108 return presenter.get_document()
111@router.get("/teams/{id}")
112async def get_team(
113 id: int,
114 database: Annotated[Database, Depends(create_database)],
115) -> TeamDocument:
116 """Get the team with the given id."""
117 presenter = JsonApiTeamPresenter()
118 command = GetTeamCommand(id=id)
119 await GetTeam(TeamDbRepository(database), presenter).execute(command)
120 return presenter.get_document()
123@router.post("/teams", status_code=status.HTTP_201_CREATED)
124async def create_team(
125 resource: TeamDocument,
126 database: Annotated[Database, Depends(create_database)],
127 user: Annotated[UserEntity, Depends(get_current_user)],
128) -> TeamDocument:
129 """Create a new team."""
130 command = CreateTeamCommand(
131 name=resource.data.attributes.name,
132 active=resource.data.attributes.active,
133 remark=resource.data.attributes.remark,
134 )
135 team_presenter = JsonApiTeamPresenter()
136 async with UnitOfWork(database):
137 await CreateTeam(TeamDbRepository(database), team_presenter).execute(command)
138 return team_presenter.get_document()
141@router.patch(
142 "/teams/{id}",
143 status_code=status.HTTP_200_OK,
144 responses={status.HTTP_404_NOT_FOUND: {"description": "Team not found"}},
145)
146async def update_team(
147 id: int,
148 resource: TeamDocument,
149 database: Annotated[Database, Depends(create_database)],
150 user: Annotated[UserEntity, Depends(get_current_user)],
151) -> TeamDocument:
152 """Update an existing team."""
153 command = UpdateTeamCommand(
154 id=id,
155 name=resource.data.attributes.name,
156 active=resource.data.attributes.active,
157 remark=resource.data.attributes.remark,
158 )
159 team_presenter = JsonApiTeamPresenter()
160 async with UnitOfWork(database):
161 await UpdateTeam(TeamDbRepository(database), team_presenter).execute(command)
162 return team_presenter.get_document()
165@router.delete(
166 "/teams/{id}",
167 status_code=status.HTTP_200_OK,
168 responses={status.HTTP_404_NOT_FOUND: {"description": "Team not found"}},
169)
170async def delete_team(
171 id: int,
172 database: Annotated[Database, Depends(create_database)],
173 user: Annotated[UserEntity, Depends(get_current_user)],
174):
175 """Delete the team with the given id."""
176 command = DeleteTeamCommand(id=id)
178 try:
179 async with UnitOfWork(database):
180 await DeleteTeam(TeamDbRepository(database)).execute(command)
181 except TeamNotFoundException as ex:
182 raise HTTPException(
183 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
184 ) from ex
187@router.get("/teams/{id}/members")
188async def get_team_members(
189 id: int,
190 database: Annotated[Database, Depends(create_database)],
191) -> TeamMembersDocument:
192 """Get the members of the team with the given id."""
193 presenter = JsonApiTeamMembersPresenter()
194 command = GetTeamCommand(id=id)
195 await GetTeam(TeamDbRepository(database), presenter).execute(command)
196 return presenter.get_document()
199@router.post("/teams/{id}/members", status_code=status.HTTP_201_CREATED)
200async def create_team_member(
201 id: int,
202 resource: TeamMemberDocument,
203 database: Annotated[Database, Depends(create_database)],
204) -> TeamMemberDocument:
205 """Add a member to the team with the given id."""
206 if resource.data.id is None:
207 raise HTTPException(
208 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
209 detail="Missing id of the team member",
210 )
212 presenter = JsonApiTeamMemberPresenter()
213 command = CreateTeamMemberCommand(
214 team_id=id,
215 member_uuid=resource.data.id,
216 active=resource.data.attributes.active,
217 )
218 try:
219 async with UnitOfWork(database):
220 await CreateTeamMember(
221 TeamDbRepository(database), MemberDbRepository(database), presenter
222 ).execute(command)
223 return presenter.get_document()
224 except TeamNotFoundException as ex:
225 raise HTTPException(
226 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
227 ) from ex
228 except MemberNotFoundException as ex:
229 raise HTTPException(
230 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
231 ) from ex
232 except TeamMemberAlreadyExistException as ex:
233 raise HTTPException(
234 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ex)
235 ) from ex
238@router.delete(
239 path="/teams/{id}/members",
240 status_code=status.HTTP_204_NO_CONTENT,
241)
242async def delete_team_members(
243 id: int,
244 database: Annotated[Database, Depends(create_database)],
245 user: Annotated[UserEntity, Depends(get_current_user)],
246 resource: DeleteTeamMembersResource,
247) -> None:
248 """Delete multiple members from a team."""
249 presenter = JsonApiTeamPresenter()
251 try:
252 async with UnitOfWork(database):
253 for team_member_identifier in resource.data:
254 if team_member_identifier.id is not None:
255 command = DeleteMemberFromTeamCommand(
256 team_id=id,
257 member_uuid=team_member_identifier.id,
258 )
259 await DeleteMemberFromTeam(
260 TeamDbRepository(database),
261 MemberDbRepository(database),
262 presenter,
263 ).execute(command)
264 except TeamNotFoundException as ex:
265 raise HTTPException(
266 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
267 ) from ex
268 except MemberNotFoundException as ex:
269 raise HTTPException(
270 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
271 ) from ex
274@router.delete(
275 "/teams/{id}/members/{member_uuid}",
276 status_code=status.HTTP_204_NO_CONTENT,
277 responses={status.HTTP_404_NOT_FOUND: {"description": "Team not found"}},
278)
279async def delete_team_member(
280 id: int,
281 member_uuid: str,
282 database: Annotated[Database, Depends(create_database)],
283 user: Annotated[UserEntity, Depends(get_current_user)],
284) -> None:
285 """Delete the member with the given unique id from the team."""
286 presenter = JsonApiTeamPresenter()
288 command = DeleteMemberFromTeamCommand(team_id=id, member_uuid=member_uuid)
289 try:
290 async with UnitOfWork(database):
291 await DeleteMemberFromTeam(
292 TeamDbRepository(database), MemberDbRepository(database), presenter
293 ).execute(command)
294 except TeamNotFoundException as ex:
295 raise HTTPException(
296 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
297 ) from ex
298 except MemberNotFoundException as ex:
299 raise HTTPException(
300 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex)
301 ) from ex