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

1"""Module that defines the teams API.""" 

2 

3from typing import Annotated 

4 

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 

27 

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) 

43 

44 

45router = APIRouter(tags=["teams"]) 

46 

47 

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() 

57 

58 

59class TeamMemberFilterModel(BaseModel): 

60 """Define the JSON:API filter for team members.""" 

61 

62 team: str | None = Field(Query(default=None, alias="filter[team]")) 

63 

64 

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 ) 

86 

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() 

109 

110 

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() 

121 

122 

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() 

139 

140 

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() 

163 

164 

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) 

177 

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 

185 

186 

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() 

197 

198 

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 ) 

211 

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 

236 

237 

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() 

250 

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 

272 

273 

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() 

287 

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