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

96 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_team import DeleteTeam, DeleteTeamCommand 

10from kwai_bc_teams.domain.team import TeamMemberAlreadyExistException 

11from kwai_bc_teams.get_members import GetMembers, GetMembersCommand 

12from kwai_bc_teams.get_team import GetTeam, GetTeamCommand 

13from kwai_bc_teams.get_teams import GetTeams, GetTeamsCommand 

14from kwai_bc_teams.repositories.member_db_repository import MemberDbRepository 

15from kwai_bc_teams.repositories.member_repository import MemberNotFoundException 

16from kwai_bc_teams.repositories.team_db_repository import TeamDbRepository 

17from kwai_bc_teams.repositories.team_repository import TeamNotFoundException 

18from kwai_bc_teams.update_team import UpdateTeam, UpdateTeamCommand 

19from kwai_core.db.database import Database 

20from kwai_core.db.uow import UnitOfWork 

21from kwai_core.json_api import PaginationModel 

22from pydantic import BaseModel, Field 

23 

24from kwai_api.dependencies import create_database, get_current_user 

25from kwai_api.v1.teams.presenters import ( 

26 JsonApiMembersPresenter, 

27 JsonApiTeamMemberPresenter, 

28 JsonApiTeamMembersPresenter, 

29 JsonApiTeamPresenter, 

30 JsonApiTeamsPresenter, 

31) 

32from kwai_api.v1.teams.schemas import ( 

33 TeamDocument, 

34 TeamMemberDocument, 

35 TeamMembersDocument, 

36 TeamsDocument, 

37) 

38 

39 

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

41 

42 

43@router.get("/teams") 

44async def get_teams( 

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

46) -> TeamsDocument: 

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

48 presenter = JsonApiTeamsPresenter() 

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

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

51 return presenter.get_document() 

52 

53 

54class TeamMemberFilterModel(BaseModel): 

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

56 

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

58 

59 

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

61async def get_members( 

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

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

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

65) -> TeamMembersDocument: 

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

67 presenter = JsonApiMembersPresenter() 

68 if team_filter.team is not None: 

69 if ":" in team_filter.team: 

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

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

72 raise HTTPException( 

73 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

74 detail="Invalid operand", 

75 ) 

76 if not team_id.isdigit(): 

77 raise HTTPException( 

78 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

79 detail="Invalid team id", 

80 ) 

81 

82 command = GetMembersCommand( 

83 team_id=int(team_id), 

84 in_team=operand == "eq", 

85 offset=pagination.offset, 

86 limit=pagination.limit, 

87 ) 

88 else: 

89 if team_filter.team.isdigit(): 

90 command = GetMembersCommand( 

91 team_id=int(team_filter.team), 

92 offset=pagination.offset, 

93 limit=pagination.limit, 

94 ) 

95 else: 

96 raise HTTPException( 

97 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

98 detail="Invalid team id", 

99 ) 

100 else: 

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

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

103 return presenter.get_document() 

104 

105 

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

107async def get_team( 

108 id: int, 

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

110) -> TeamDocument: 

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

112 presenter = JsonApiTeamPresenter() 

113 command = GetTeamCommand(id=id) 

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

115 return presenter.get_document() 

116 

117 

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

119async def create_team( 

120 resource: TeamDocument, 

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

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

123) -> TeamDocument: 

124 """Create a new team.""" 

125 command = CreateTeamCommand( 

126 name=resource.data.attributes.name, 

127 active=resource.data.attributes.active, 

128 remark=resource.data.attributes.remark, 

129 ) 

130 team_presenter = JsonApiTeamPresenter() 

131 async with UnitOfWork(database): 

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

133 return team_presenter.get_document() 

134 

135 

136@router.patch( 

137 "/teams/{id}", 

138 status_code=status.HTTP_200_OK, 

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

140) 

141async def update_team( 

142 id: int, 

143 resource: TeamDocument, 

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

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

146) -> TeamDocument: 

147 """Update an existing team.""" 

148 command = UpdateTeamCommand( 

149 id=id, 

150 name=resource.data.attributes.name, 

151 active=resource.data.attributes.active, 

152 remark=resource.data.attributes.remark, 

153 ) 

154 team_presenter = JsonApiTeamPresenter() 

155 async with UnitOfWork(database): 

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

157 return team_presenter.get_document() 

158 

159 

160@router.delete( 

161 "/teams/{id}", 

162 status_code=status.HTTP_200_OK, 

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

164) 

165async def delete_team( 

166 id: int, 

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

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

169): 

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

171 command = DeleteTeamCommand(id=id) 

172 

173 try: 

174 async with UnitOfWork(database): 

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

176 except TeamNotFoundException as ex: 

177 raise HTTPException( 

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

179 ) from ex 

180 

181 

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

183async def get_team_members( 

184 id: int, 

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

186) -> TeamMembersDocument: 

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

188 presenter = JsonApiTeamMembersPresenter() 

189 command = GetTeamCommand(id=id) 

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

191 return presenter.get_document() 

192 

193 

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

195async def create_team_member( 

196 id: int, 

197 resource: TeamMemberDocument, 

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

199) -> TeamMemberDocument: 

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

201 presenter = JsonApiTeamMemberPresenter() 

202 command = CreateTeamMemberCommand( 

203 team_id=id, 

204 member_id=resource.data.id, 

205 active=resource.data.attributes.active, 

206 ) 

207 try: 

208 async with UnitOfWork(database): 

209 await CreateTeamMember( 

210 TeamDbRepository(database), MemberDbRepository(database), presenter 

211 ).execute(command) 

212 return presenter.get_document() 

213 except TeamNotFoundException as ex: 

214 raise HTTPException( 

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

216 ) from ex 

217 except MemberNotFoundException as ex: 

218 raise HTTPException( 

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

220 ) from ex 

221 except TeamMemberAlreadyExistException as ex: 

222 raise HTTPException( 

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

224 ) from ex