Coverage for src/kwai/api/v1/teams/api.py: 85%

96 statements  

« prev     ^ index     » next       coverage.py v7.6.10, 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 pydantic import BaseModel, Field 

7 

8from kwai.api.dependencies import create_database, get_current_user 

9from kwai.api.v1.teams.presenters import ( 

10 JsonApiMembersPresenter, 

11 JsonApiTeamMemberPresenter, 

12 JsonApiTeamMembersPresenter, 

13 JsonApiTeamPresenter, 

14 JsonApiTeamsPresenter, 

15) 

16from kwai.api.v1.teams.schemas import TeamDocument, TeamMemberDocument 

17from kwai.core.db.database import Database 

18from kwai.core.db.uow import UnitOfWork 

19from kwai.core.json_api import PaginationModel 

20from kwai.modules.identity.users.user import UserEntity 

21from kwai.modules.teams.create_team import CreateTeam, CreateTeamCommand 

22from kwai.modules.teams.create_team_member import ( 

23 CreateTeamMember, 

24 CreateTeamMemberCommand, 

25) 

26from kwai.modules.teams.delete_team import DeleteTeam, DeleteTeamCommand 

27from kwai.modules.teams.domain.team import TeamMemberAlreadyExistException 

28from kwai.modules.teams.get_members import GetMembers, GetMembersCommand 

29from kwai.modules.teams.get_team import GetTeam, GetTeamCommand 

30from kwai.modules.teams.get_teams import GetTeams, GetTeamsCommand 

31from kwai.modules.teams.repositories.member_db_repository import MemberDbRepository 

32from kwai.modules.teams.repositories.member_repository import MemberNotFoundException 

33from kwai.modules.teams.repositories.team_db_repository import TeamDbRepository 

34from kwai.modules.teams.update_team import UpdateTeam, UpdateTeamCommand 

35from kwai.modules.training.teams.team_repository import TeamNotFoundException 

36 

37 

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

39 

40 

41@router.get("/teams") 

42async def get_teams( 

43 database: Annotated[Database, Depends(create_database)], 

44) -> TeamDocument: 

45 """Get all teams of the club.""" 

46 presenter = JsonApiTeamsPresenter() 

47 command = GetTeamsCommand(offset=0, limit=0) 

48 await GetTeams(TeamDbRepository(database), presenter).execute(command) 

49 return presenter.get_document() 

50 

51 

52class TeamMemberFilterModel(BaseModel): 

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

54 

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

56 

57 

58@router.get("/teams/members") 

59async def get_members( 

60 database: Annotated[Database, Depends(create_database)], 

61 pagination: Annotated[PaginationModel, Depends(PaginationModel)], 

62 team_filter: Annotated[TeamMemberFilterModel, Depends(TeamMemberFilterModel)], 

63) -> TeamMemberDocument: 

64 """Get all members that can be part of a team.""" 

65 presenter = JsonApiMembersPresenter() 

66 if team_filter.team is not None: 

67 if ":" in team_filter.team: 

68 operand, team_id = team_filter.team.split(":") 

69 if operand not in ("eq", "noteq"): 

70 raise HTTPException( 

71 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

72 detail="Invalid operand", 

73 ) 

74 if not team_id.isdigit(): 

75 raise HTTPException( 

76 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

77 detail="Invalid team id", 

78 ) 

79 

80 command = GetMembersCommand( 

81 team_id=int(team_id), 

82 in_team=operand == "eq", 

83 offset=pagination.offset, 

84 limit=pagination.limit, 

85 ) 

86 else: 

87 if team_filter.team.isdigit(): 

88 command = GetMembersCommand( 

89 team_id=int(team_filter.team), 

90 offset=pagination.offset, 

91 limit=pagination.limit, 

92 ) 

93 else: 

94 raise HTTPException( 

95 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

96 detail="Invalid team id", 

97 ) 

98 else: 

99 command = GetMembersCommand(offset=pagination.offset, limit=pagination.limit) 

100 await GetMembers(MemberDbRepository(database), presenter).execute(command) 

101 return presenter.get_document() 

102 

103 

104@router.get("/teams/{id}") 

105async def get_team( 

106 id: int, 

107 database: Annotated[Database, Depends(create_database)], 

108) -> TeamDocument: 

109 """Get the team with the given id.""" 

110 presenter = JsonApiTeamPresenter() 

111 command = GetTeamCommand(id=id) 

112 await GetTeam(TeamDbRepository(database), presenter).execute(command) 

113 return presenter.get_document() 

114 

115 

116@router.post("/teams", status_code=status.HTTP_201_CREATED) 

117async def create_team( 

118 resource: TeamDocument, 

119 database: Annotated[Database, Depends(create_database)], 

120 user: Annotated[UserEntity, Depends(get_current_user)], 

121) -> TeamDocument: 

122 """Create a new team.""" 

123 command = CreateTeamCommand( 

124 name=resource.data.attributes.name, 

125 active=resource.data.attributes.active, 

126 remark=resource.data.attributes.remark, 

127 ) 

128 team_presenter = JsonApiTeamPresenter() 

129 async with UnitOfWork(database): 

130 await CreateTeam(TeamDbRepository(database), team_presenter).execute(command) 

131 return team_presenter.get_document() 

132 

133 

134@router.patch( 

135 "/teams/{id}", 

136 status_code=status.HTTP_200_OK, 

137 responses={status.HTTP_404_NOT_FOUND: {"description": "Team not found"}}, 

138) 

139async def update_team( 

140 id: int, 

141 resource: TeamDocument, 

142 database: Annotated[Database, Depends(create_database)], 

143 user: Annotated[UserEntity, Depends(get_current_user)], 

144) -> TeamDocument: 

145 """Update an existing team.""" 

146 command = UpdateTeamCommand( 

147 id=id, 

148 name=resource.data.attributes.name, 

149 active=resource.data.attributes.active, 

150 remark=resource.data.attributes.remark, 

151 ) 

152 team_presenter = JsonApiTeamPresenter() 

153 async with UnitOfWork(database): 

154 await UpdateTeam(TeamDbRepository(database), team_presenter).execute(command) 

155 return team_presenter.get_document() 

156 

157 

158@router.delete( 

159 "/teams/{id}", 

160 status_code=status.HTTP_200_OK, 

161 responses={status.HTTP_404_NOT_FOUND: {"description": "Team not found"}}, 

162) 

163async def delete_team( 

164 id: int, 

165 database: Annotated[Database, Depends(create_database)], 

166 user: Annotated[UserEntity, Depends(get_current_user)], 

167): 

168 """Delete the team with the given id.""" 

169 command = DeleteTeamCommand(id=id) 

170 

171 try: 

172 async with UnitOfWork(database): 

173 await DeleteTeam(TeamDbRepository(database)).execute(command) 

174 except TeamNotFoundException as ex: 

175 raise HTTPException( 

176 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex) 

177 ) from ex 

178 

179 

180@router.get("/teams/{id}/members") 

181async def get_team_members( 

182 id: int, 

183 database: Annotated[Database, Depends(create_database)], 

184) -> TeamMemberDocument: 

185 """Get the members of the team with the given id.""" 

186 presenter = JsonApiTeamMembersPresenter() 

187 command = GetTeamCommand(id=id) 

188 await GetTeam(TeamDbRepository(database), presenter).execute(command) 

189 return presenter.get_document() 

190 

191 

192@router.post("/teams/{id}/members", status_code=status.HTTP_201_CREATED) 

193async def create_team_member( 

194 id: int, 

195 resource: TeamMemberDocument, 

196 database: Annotated[Database, Depends(create_database)], 

197) -> TeamMemberDocument: 

198 """Add a member to the team with the given id.""" 

199 presenter = JsonApiTeamMemberPresenter() 

200 command = CreateTeamMemberCommand( 

201 team_id=id, 

202 member_id=resource.data.id, 

203 active=resource.data.attributes.active, 

204 ) 

205 try: 

206 async with UnitOfWork(database): 

207 await CreateTeamMember( 

208 TeamDbRepository(database), MemberDbRepository(database), presenter 

209 ).execute(command) 

210 return presenter.get_document() 

211 except TeamNotFoundException as ex: 

212 raise HTTPException( 

213 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex) 

214 ) from ex 

215 except MemberNotFoundException as ex: 

216 raise HTTPException( 

217 status_code=status.HTTP_404_NOT_FOUND, detail=str(ex) 

218 ) from ex 

219 except TeamMemberAlreadyExistException as ex: 

220 raise HTTPException( 

221 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ex) 

222 ) from ex