Coverage for polar/file/endpoints.py: 55%

45 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-12-05 17:15 +0000

1from typing import Annotated 1a

2 

3from fastapi import Depends, Path, Query 1a

4from pydantic import UUID4 1a

5 

6from polar.exceptions import NotPermitted, ResourceNotFound 1a

7from polar.kit.pagination import ListResource, PaginationParamsQuery 1a

8from polar.kit.schemas import MultipleQueryFilter 1a

9from polar.models import File 1a

10from polar.openapi import APITag 1a

11from polar.organization.resolver import get_payload_organization 1a

12from polar.organization.schemas import OrganizationID 1a

13from polar.postgres import ( 1a

14 AsyncReadSession, 

15 AsyncSession, 

16 get_db_read_session, 

17 get_db_session, 

18) 

19from polar.routing import APIRouter 1a

20 

21from . import auth 1a

22from .schemas import ( 1a

23 FileCreate, 

24 FilePatch, 

25 FileRead, 

26 FileReadAdapter, 

27 FileUpload, 

28 FileUploadCompleted, 

29) 

30from .service import file as file_service 1a

31 

32router = APIRouter(prefix="/files", tags=["files", APITag.public]) 1a

33 

34FileID = Annotated[UUID4, Path(description="The file ID.")] 1a

35FileNotFound = {"description": "File not found.", "model": ResourceNotFound.schema()} 1a

36 

37 

38@router.get("/", summary="List Files", response_model=ListResource[FileRead]) 1a

39async def list( 1a

40 auth_subject: auth.FileRead, 

41 pagination: PaginationParamsQuery, 

42 organization_id: MultipleQueryFilter[OrganizationID] | None = Query( 

43 None, title="OrganizationID Filter", description="Filter by organization ID." 

44 ), 

45 ids: MultipleQueryFilter[UUID4] | None = Query( 

46 None, title="FileID Filter", description="Filter by file ID." 

47 ), 

48 session: AsyncReadSession = Depends(get_db_read_session), 

49) -> ListResource[FileRead]: 

50 """List files.""" 

51 results, count = await file_service.list( 

52 session, 

53 auth_subject, 

54 organization_id=organization_id, 

55 ids=ids, 

56 pagination=pagination, 

57 ) 

58 return ListResource.from_paginated_results( 

59 [FileReadAdapter.validate_python(result) for result in results], 

60 count, 

61 pagination, 

62 ) 

63 

64 

65@router.post( 1a

66 "/", 

67 response_model=FileUpload, 

68 summary="Create File", 

69 status_code=201, 

70 responses={201: {"description": "File created."}}, 

71) 

72async def create( 1a

73 file_create: FileCreate, 

74 auth_subject: auth.FileWrite, 

75 session: AsyncSession = Depends(get_db_session), 

76) -> FileUpload: 

77 """Create a file.""" 

78 organization = await get_payload_organization(session, auth_subject, file_create) 

79 

80 file_create.organization_id = organization.id 

81 return await file_service.generate_presigned_upload( 

82 session, 

83 organization=organization, 

84 create_schema=file_create, 

85 ) 

86 

87 

88@router.post( 1a

89 "/{id}/uploaded", 

90 summary="Complete File Upload", 

91 response_model=FileRead, 

92 responses={ 

93 200: {"description": "File upload completed."}, 

94 403: { 

95 "description": "You don't have the permission to update this file.", 

96 "model": NotPermitted.schema(), 

97 }, 

98 404: FileNotFound, 

99 }, 

100) 

101async def uploaded( 1a

102 id: FileID, 

103 completed_schema: FileUploadCompleted, 

104 auth_subject: auth.FileWrite, 

105 session: AsyncSession = Depends(get_db_session), 

106) -> File: 

107 """Complete a file upload.""" 

108 file = await file_service.get(session, auth_subject, id) 

109 if file is None: 

110 raise ResourceNotFound() 

111 

112 return await file_service.complete_upload( 

113 session, file=file, completed_schema=completed_schema 

114 ) 

115 

116 

117# Re-introduce with changing version 

118@router.patch( 1a

119 "/{id}", 

120 summary="Update File", 

121 response_model=FileRead, 

122 responses={ 

123 200: {"description": "File updated."}, 

124 403: { 

125 "description": "You don't have the permission to update this file.", 

126 "model": NotPermitted.schema(), 

127 }, 

128 404: FileNotFound, 

129 }, 

130) 

131async def update( 1a

132 auth_subject: auth.FileWrite, 

133 id: FileID, 

134 patches: FilePatch, 

135 session: AsyncSession = Depends(get_db_session), 

136) -> File: 

137 """Update a file.""" 

138 file = await file_service.get(session, auth_subject, id) 

139 if file is None: 

140 raise ResourceNotFound() 

141 

142 return await file_service.patch(session, file=file, patches=patches) 

143 

144 

145@router.delete( 1a

146 "/{id}", 

147 summary="Delete File", 

148 status_code=204, 

149 responses={ 

150 204: {"description": "File deleted."}, 

151 403: { 

152 "description": "You don't have the permission to delete this file.", 

153 "model": NotPermitted.schema(), 

154 }, 

155 404: FileNotFound, 

156 }, 

157) 

158async def delete( 1a

159 auth_subject: auth.FileWrite, 

160 id: UUID4, 

161 session: AsyncSession = Depends(get_db_session), 

162) -> None: 

163 """Delete a file.""" 

164 file = await file_service.get(session, auth_subject, id) 

165 if file is None: 

166 raise ResourceNotFound() 

167 

168 await file_service.delete(session, file=file)