Coverage for polar/integrations/github_repository_benefit/endpoints.py: 52%

63 statements  

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

1from typing import Any 1a

2 

3from fastapi import Depends, Request, Response 1a

4from fastapi.responses import RedirectResponse 1a

5from httpx_oauth.integrations.fastapi import OAuth2AuthorizeCallback 1a

6from httpx_oauth.oauth2 import OAuth2Token 1a

7 

8from polar.auth.dependencies import WebUserWrite 1a

9from polar.config import settings 1a

10from polar.eventstream.service import publish 1a

11from polar.exceptions import ( 1a

12 NotPermitted, 

13 PolarRedirectionError, 

14 ResourceAlreadyExists, 

15 Unauthorized, 

16) 

17from polar.integrations.github_repository_benefit.schemas import ( 1a

18 GitHubInvitesBenefitRepositories, 

19) 

20from polar.kit import jwt 1a

21from polar.kit.http import ReturnTo, add_query_parameters, get_safe_return_url 1a

22from polar.openapi import APITag 1a

23from polar.postgres import AsyncSession, get_db_session 1a

24from polar.redis import Redis, get_redis 1a

25from polar.routing import APIRouter 1a

26 

27from .service import github_oauth_client, github_repository_benefit_user_service 1a

28 

29router = APIRouter( 1a

30 prefix="/integrations/github_repository_benefit", 

31 tags=["integrations_github_repository_benefit", APITag.private], 

32) 

33 

34 

35############################################################################### 

36# OAUTH2 

37############################################################################### 

38 

39 

40def get_decoded_token_state( 1a

41 access_token_state: tuple[OAuth2Token, str | None], 

42) -> tuple[OAuth2Token, dict[str, Any]]: 

43 token_data, state = access_token_state 

44 if not state: 

45 raise Unauthorized("No state") 

46 

47 try: 

48 state_data = jwt.decode( 

49 token=state, 

50 secret=settings.SECRET, 

51 type="github_repository_benefit_oauth", 

52 ) 

53 except jwt.DecodeError as e: 

54 raise Unauthorized("Invalid state") from e 

55 

56 return (token_data, state_data) 

57 

58 

59############################################################################### 

60# User OAuth 

61############################################################################### 

62 

63 

64oauth2_authorize_callback = OAuth2AuthorizeCallback( 1a

65 github_oauth_client, 

66 route_name="integrations.github_repository_benefit.user_callback", 

67) 

68 

69 

70class OAuthCallbackError(PolarRedirectionError): ... 1a

71 

72 

73class NotPermittedOrganizationBillingPlan(NotPermitted): 1a

74 def __init__(self) -> None: 1a

75 message = "Organization billing plan not accessible." 

76 super().__init__(message) 

77 

78 

79@router.get( 1a

80 "/user/authorize", name="integrations.github_repository_benefit.user_authorize" 

81) 

82async def user_authorize( 1a

83 request: Request, return_to: ReturnTo, auth_subject: WebUserWrite 

84) -> RedirectResponse: 

85 state = {"return_to": return_to} 

86 

87 encoded_state = jwt.encode( 

88 data=state, secret=settings.SECRET, type="github_repository_benefit_oauth" 

89 ) 

90 

91 authorization_url = await github_oauth_client.get_authorization_url( 

92 redirect_uri=str( 

93 request.url_for("integrations.github_repository_benefit.user_callback") 

94 ), 

95 state=encoded_state, 

96 scope=["user", "user.email"], 

97 ) 

98 return RedirectResponse(authorization_url, 303) 

99 

100 

101@router.get( 1a

102 "/user/callback", name="integrations.github_repository_benefit.user_callback" 

103) 

104async def user_callback( 1a

105 auth_subject: WebUserWrite, 

106 session: AsyncSession = Depends(get_db_session), 

107 access_token_state: tuple[OAuth2Token, str | None] = Depends( 

108 oauth2_authorize_callback 

109 ), 

110) -> RedirectResponse: 

111 token_data, state = get_decoded_token_state(access_token_state) 

112 

113 try: 

114 await github_repository_benefit_user_service.create_oauth_account( 

115 session, 

116 auth_subject.subject, 

117 token_data, 

118 ) 

119 except ResourceAlreadyExists: 

120 await github_repository_benefit_user_service.update_oauth_account( 

121 session, auth_subject.subject, token_data 

122 ) 

123 

124 return_to = state["return_to"] 

125 redirect_url = get_safe_return_url(add_query_parameters(return_to)) 

126 

127 return RedirectResponse(redirect_url, 303) 

128 

129 

130@router.get( 1a

131 "/user/repositories", 

132 name="integrations.github_repository_benefit.user_repositories", 

133 description="Lists available repositories for this user", 

134) 

135async def user_repositories( 1a

136 auth_subject: WebUserWrite, 

137 session: AsyncSession = Depends(get_db_session), 

138 redis: Redis = Depends(get_redis), 

139) -> GitHubInvitesBenefitRepositories: 

140 oauth = await github_repository_benefit_user_service.get_oauth_account( 

141 session, auth_subject.subject 

142 ) 

143 

144 installations = ( 

145 await github_repository_benefit_user_service.list_user_installations(oauth) 

146 ) 

147 

148 orgs = await github_repository_benefit_user_service.list_orgs_with_billing_plans( 

149 redis, oauth, installations 

150 ) 

151 

152 repos = await github_repository_benefit_user_service.list_repositories( 

153 oauth, 

154 installations, 

155 ) 

156 

157 return GitHubInvitesBenefitRepositories( 

158 organizations=orgs, 

159 repositories=repos, 

160 ) 

161 

162 

163############################################################################### 

164# Installation 

165############################################################################### 

166 

167 

168@router.get( 1a

169 "/installation/install", 

170 name="integrations.github_repository_benefit.installation_install", 

171) 

172async def installation_install( 1a

173 request: Request, auth_subject: WebUserWrite 

174) -> RedirectResponse: 

175 return RedirectResponse( 

176 f"https://github.com/apps/{settings.GITHUB_REPOSITORY_BENEFITS_APP_NAMESPACE}/installations/new", 

177 303, 

178 ) 

179 

180 

181@router.get( 1a

182 "/installation/callback", 

183 name="integrations.github_repository_benefit.installation_callback", 

184) 

185async def installation_callback( 1a

186 request: Request, auth_subject: WebUserWrite 

187) -> Response: 

188 await publish( 

189 "integrations.github_repository_benefit.installed", 

190 payload={}, 

191 user_id=auth_subject.subject.id, 

192 ) 

193 

194 return Response("Installation successful, you can close this page.")