Coverage for apps/kwai-api/src/kwai_api/v1/club/coaches/endpoints.py: 87%

62 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2024-01-01 00:00 +0000

1"""Module for defining endpoints for coaches of the club API.""" 

2 

3from typing import Annotated 

4 

5from fastapi import APIRouter, Depends, HTTPException, Query, status 

6from kwai_bc_club.create_coach import CreateCoach, CreateCoachCommand 

7from kwai_bc_club.get_coach import GetCoach, GetCoachCommand 

8from kwai_bc_club.get_coaches import GetCoaches, GetCoachesCommand 

9from kwai_bc_club.repositories.coach_db_repository import CoachDbRepository 

10from kwai_bc_club.repositories.coach_repository import ( 

11 CoachAlreadyExistException, 

12 CoachNotFoundException, 

13) 

14from kwai_bc_club.repositories.member_db_repository import MemberDbRepository 

15from kwai_bc_club.repositories.member_repository import MemberNotFoundException 

16from kwai_bc_club.update_coach import UpdateCoach, UpdateCoachCommand 

17from kwai_bc_identity.users.user import UserEntity 

18from kwai_core.db.database import Database 

19from kwai_core.db.uow import UnitOfWork 

20from kwai_core.json_api import PaginationModel 

21from pydantic import BaseModel, Field 

22 

23from kwai_api.dependencies import create_database, get_current_user 

24from kwai_api.v1.club.coaches.presenters import ( 

25 JsonApiCoachesPresenter, 

26 JsonApiCoachPresenter, 

27) 

28from kwai_api.v1.club.coaches.schemas import CoachDocument, CoachesDocument 

29 

30 

31router = APIRouter() 

32 

33 

34class CoachesFilterModel(BaseModel): 

35 """JSON:API filter for coaches.""" 

36 

37 enabled: bool = Field(Query(default=True, alias="filter[enabled]")) 

38 

39 

40@router.get("/coaches") 

41async def get_coaches( 

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

43 coaches_filter: Annotated[CoachesFilterModel, Depends(CoachesFilterModel)], 

44 db: Annotated[Database, Depends(create_database)], 

45 user: Annotated[UserEntity | None, Depends(get_current_user)], 

46) -> CoachesDocument: 

47 """Get all coaches.""" 

48 presenter = JsonApiCoachesPresenter() 

49 active = coaches_filter.enabled 

50 

51 command = GetCoachesCommand( 

52 offset=pagination.offset, limit=pagination.limit, active=active 

53 ) 

54 await GetCoaches(CoachDbRepository(db), presenter).execute(command) 

55 

56 return presenter.get_document() 

57 

58 

59@router.get("/coaches/{coach_uuid}") 

60async def get_coach( 

61 coach_uuid: str, 

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

63 user: Annotated[UserEntity | None, Depends(get_current_user)], 

64) -> CoachDocument: 

65 """Get a coach. 

66 

67 The unique id of the member is used to retrieve the coach. 

68 """ 

69 presenter = JsonApiCoachPresenter() 

70 command = GetCoachCommand(uuid=coach_uuid) 

71 await GetCoach(CoachDbRepository(db), presenter).execute(command) 

72 

73 return presenter.get_document() 

74 

75 

76@router.post( 

77 "/coaches", 

78 responses={ 

79 status.HTTP_201_CREATED: {"description": "Coach was created."}, 

80 status.HTTP_404_NOT_FOUND: {"description": "Member is not found."}, 

81 status.HTTP_409_CONFLICT: {"description": "Coach already exists."}, 

82 status.HTTP_422_UNPROCESSABLE_ENTITY: {"description": "Data is invalid."}, 

83 }, 

84 status_code=status.HTTP_201_CREATED, 

85) 

86async def create_coach( 

87 db: Annotated[Database, Depends(create_database)], 

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

89 resource: CoachDocument, 

90) -> CoachDocument: 

91 """Create a new coach.""" 

92 if ( 

93 resource.data.relationships is None 

94 or resource.data.relationships.member is None 

95 or resource.data.relationships.member.data is None 

96 or resource.data.relationships.member.data.id is None 

97 ): 

98 raise HTTPException( 

99 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 

100 detail="Member relationship data is missing.", 

101 ) 

102 

103 command = CreateCoachCommand( 

104 description=resource.data.attributes.description, 

105 diploma=resource.data.attributes.diploma, 

106 remark=resource.data.attributes.remark, 

107 active=resource.data.attributes.active, 

108 head=resource.data.attributes.head, 

109 member_uuid=resource.data.relationships.member.data.id, 

110 ) 

111 presenter = JsonApiCoachPresenter() 

112 

113 async with UnitOfWork(db): 

114 try: 

115 await CreateCoach( 

116 CoachDbRepository(db), MemberDbRepository(db), presenter 

117 ).execute(command) 

118 except MemberNotFoundException as ex: 

119 raise HTTPException( 

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

121 ) from ex 

122 except CoachAlreadyExistException as ex: 

123 raise HTTPException( 

124 status_code=status.HTTP_409_CONFLICT, detail=str(ex) 

125 ) from ex 

126 except ValueError as ve: 

127 raise HTTPException( 

128 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ve) 

129 ) from ve 

130 

131 return presenter.get_document() 

132 

133 

134@router.patch( 

135 "/coaches/{coach_uuid}", 

136 responses={status.HTTP_404_NOT_FOUND: {"description": "Coach was not found."}}, 

137) 

138async def update_coach( 

139 db: Annotated[Database, Depends(create_database)], 

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

141 coach_uuid: str, 

142 resource: CoachDocument, 

143) -> CoachDocument: 

144 """Update a coach.""" 

145 command = UpdateCoachCommand( 

146 uuid=coach_uuid, 

147 diploma=resource.data.attributes.diploma, 

148 description=resource.data.attributes.description, 

149 active=resource.data.attributes.active, 

150 remark=resource.data.attributes.remark, 

151 head=resource.data.attributes.head, 

152 ) 

153 presenter = JsonApiCoachPresenter() 

154 async with UnitOfWork(db): 

155 try: 

156 await UpdateCoach(CoachDbRepository(db), presenter).execute(command) 

157 except CoachNotFoundException as ex: 

158 raise HTTPException( 

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

160 ) from ex 

161 except ValueError as ve: 

162 raise HTTPException( 

163 status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(ve) 

164 ) from ve 

165 

166 return presenter.get_document()