Skip to content

Commit 27643fb

Browse files
authored
Merge pull request #261 from DevanshuNEU/refactor/extract-github-service
refactor: extract GitHub DB ops to service layer (OPE-80)
2 parents fdcc19d + 6c042bb commit 27643fb

2 files changed

Lines changed: 82 additions & 73 deletions

File tree

backend/routes/github.py

Lines changed: 10 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
from middleware.auth import require_auth, AuthContext
1919
from services.github import GitHubService
20+
from services.github_connections import (
21+
get_connection, save_connection, delete_connection, update_last_used,
22+
)
2023
from services.observability import logger
2124

2225

@@ -62,79 +65,13 @@ class GitHubRepoResponse(BaseModel):
6265
owner_avatar: str
6366

6467

65-
async def _get_github_connection(user_id: str) -> Optional[dict]:
66-
"""Get user's GitHub connection from database"""
67-
try:
68-
from services.supabase_service import get_supabase_service
69-
db = get_supabase_service().client
70-
result = db.table("github_connections").select("*").eq("user_id", user_id).execute()
71-
return result.data[0] if result.data else None
72-
except Exception as e:
73-
logger.error("Failed to get GitHub connection", error=str(e), user_id=user_id)
74-
return None
75-
76-
77-
async def _save_github_connection(
78-
user_id: str,
79-
access_token: str,
80-
github_user_id: int,
81-
github_username: str,
82-
github_avatar_url: Optional[str],
83-
scope: str
84-
) -> bool:
85-
"""Save or update GitHub connection in database"""
86-
try:
87-
from services.supabase_service import get_supabase_service
88-
db = get_supabase_service().client
89-
90-
data = {
91-
"user_id": user_id,
92-
"access_token": access_token,
93-
"github_user_id": github_user_id,
94-
"github_username": github_username,
95-
"github_avatar_url": github_avatar_url,
96-
"token_scope": scope,
97-
}
98-
99-
db.table("github_connections").upsert(data, on_conflict="user_id").execute()
100-
return True
101-
except Exception as e:
102-
logger.error("Failed to save GitHub connection", error=str(e), user_id=user_id)
103-
return False
104-
105-
106-
async def _delete_github_connection(user_id: str) -> bool:
107-
"""Remove GitHub connection"""
108-
try:
109-
from services.supabase_service import get_supabase_service
110-
db = get_supabase_service().client
111-
db.table("github_connections").delete().eq("user_id", user_id).execute()
112-
return True
113-
except Exception as e:
114-
logger.error("Failed to delete GitHub connection", error=str(e), user_id=user_id)
115-
return False
116-
117-
118-
async def _update_last_used(user_id: str) -> None:
119-
"""Update last_used_at timestamp"""
120-
try:
121-
from datetime import datetime, timezone
122-
from services.supabase_service import get_supabase_service
123-
db = get_supabase_service().client
124-
db.table("github_connections").update(
125-
{"last_used_at": datetime.now(timezone.utc).isoformat()}
126-
).eq("user_id", user_id).execute()
127-
except Exception:
128-
pass
129-
130-
13168
@router.get("/status", response_model=GitHubStatusResponse)
13269
async def get_github_status(auth: AuthContext = Depends(require_auth)):
13370
"""Check if user has GitHub connected and token is valid"""
13471
if not auth.user_id:
13572
raise HTTPException(status_code=401, detail="User ID required")
13673

137-
connection = await _get_github_connection(auth.user_id)
74+
connection = get_connection(auth.user_id)
13875
if not connection:
13976
return GitHubStatusResponse(connected=False)
14077

@@ -145,7 +82,7 @@ async def get_github_status(auth: AuthContext = Depends(require_auth)):
14582

14683
if not is_valid:
14784
# Token expired or revoked, clean up
148-
await _delete_github_connection(auth.user_id)
85+
delete_connection(auth.user_id)
14986
return GitHubStatusResponse(connected=False)
15087

15188
return GitHubStatusResponse(
@@ -262,7 +199,7 @@ async def github_oauth_callback(
262199
raise HTTPException(status_code=400, detail="Failed to get GitHub user info")
263200

264201
# Save connection to database
265-
saved = await _save_github_connection(
202+
saved = save_connection(
266203
user_id=auth.user_id,
267204
access_token=access_token,
268205
github_user_id=user_info.id,
@@ -289,7 +226,7 @@ async def disconnect_github(auth: AuthContext = Depends(require_auth)):
289226
if not auth.user_id:
290227
raise HTTPException(status_code=401, detail="User ID required")
291228

292-
deleted = await _delete_github_connection(auth.user_id)
229+
deleted = delete_connection(auth.user_id)
293230
return {"success": deleted}
294231

295232

@@ -309,7 +246,7 @@ async def list_github_repos(
309246
if not auth.user_id:
310247
raise HTTPException(status_code=401, detail="User ID required")
311248

312-
connection = await _get_github_connection(auth.user_id)
249+
connection = get_connection(auth.user_id)
313250
if not connection:
314251
raise HTTPException(
315252
status_code=400,
@@ -325,7 +262,7 @@ async def list_github_repos(
325262
)
326263

327264
# Update last used timestamp
328-
await _update_last_used(auth.user_id)
265+
update_last_used(auth.user_id)
329266

330267
return [
331268
GitHubRepoResponse(
@@ -351,7 +288,7 @@ async def list_github_repos(
351288

352289
# Check if token was revoked
353290
if "401" in str(e) or "Bad credentials" in str(e):
354-
await _delete_github_connection(auth.user_id)
291+
delete_connection(auth.user_id)
355292
raise HTTPException(
356293
status_code=401,
357294
detail="GitHub access revoked. Please reconnect your GitHub account."
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""
2+
GitHub Connection Service
3+
Manages GitHub OAuth connections in the database.
4+
5+
Routes orchestrate HTTP/auth flow. This service handles persistence.
6+
"""
7+
from typing import Optional
8+
from datetime import datetime, timezone
9+
10+
from services.observability import logger
11+
from services.supabase_service import get_supabase_service
12+
13+
14+
def get_connection(user_id: str) -> Optional[dict]:
15+
"""Get user's GitHub connection from database."""
16+
try:
17+
db = get_supabase_service().client
18+
result = db.table("github_connections").select("*").eq("user_id", user_id).execute()
19+
return result.data[0] if result.data else None
20+
except Exception as e:
21+
logger.error("Failed to get GitHub connection", error=str(e), user_id=user_id)
22+
return None
23+
24+
25+
def save_connection(
26+
user_id: str,
27+
access_token: str,
28+
github_user_id: int,
29+
github_username: str,
30+
github_avatar_url: Optional[str],
31+
scope: str,
32+
) -> bool:
33+
"""Save or update GitHub connection in database."""
34+
try:
35+
db = get_supabase_service().client
36+
37+
data = {
38+
"user_id": user_id,
39+
"access_token": access_token,
40+
"github_user_id": github_user_id,
41+
"github_username": github_username,
42+
"github_avatar_url": github_avatar_url,
43+
"token_scope": scope,
44+
}
45+
46+
db.table("github_connections").upsert(data, on_conflict="user_id").execute()
47+
return True
48+
except Exception as e:
49+
logger.error("Failed to save GitHub connection", error=str(e), user_id=user_id)
50+
return False
51+
52+
53+
def delete_connection(user_id: str) -> bool:
54+
"""Remove GitHub connection."""
55+
try:
56+
db = get_supabase_service().client
57+
db.table("github_connections").delete().eq("user_id", user_id).execute()
58+
return True
59+
except Exception as e:
60+
logger.error("Failed to delete GitHub connection", error=str(e), user_id=user_id)
61+
return False
62+
63+
64+
def update_last_used(user_id: str) -> None:
65+
"""Update last_used_at timestamp."""
66+
try:
67+
db = get_supabase_service().client
68+
db.table("github_connections").update(
69+
{"last_used_at": datetime.now(timezone.utc).isoformat()}
70+
).eq("user_id", user_id).execute()
71+
except Exception as e:
72+
logger.debug("Failed to update last_used_at", user_id=user_id, error=str(e))

0 commit comments

Comments
 (0)