Skip to content

Commit 2636a8f

Browse files
committed
feat: Git Trees API for /files, drop traverse_cap; docs + openapi
Made-with: Cursor
1 parent 357a3dd commit 2636a8f

8 files changed

Lines changed: 1157 additions & 965 deletions

File tree

docs/API_GitHub_Spec.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,18 @@ curl -X PUT "https://example.com/api/user/selected-repos" \
190190
### 3.1 GET `/api/github/repos/{repo_id}/files`
191191

192192
- **설명:** 레포의 파일/디렉터리 트리 조회 (임베딩·포트폴리오 대상 선택용)
193+
- **구현:** GitHub **Git Trees API** 단일 `recursive=1` 호출로 전체 트리를 가져온 뒤, 서버에서 `path`·`depth`으로 필터링한다.
194+
1. `ref`가 없으면 `GET /repos/{owner}/{repo}``default_branch`를 구한다.
195+
2. 브랜치명이면 `GET /repos/{owner}/{repo}/git/ref/heads/{branch}`로 커밋 SHA를 구한다. (커밋 SHA 형태면 `GET /repos/{owner}/{repo}/git/commits/{sha}`로 바로 사용)
196+
3. `GET /repos/{owner}/{repo}/git/commits/{commit_sha}`**tree SHA**를 구한다.
197+
4. `GET /repos/{owner}/{repo}/git/trees/{tree_sha}?recursive=1` 한 번으로 평탄(flat) 트리를 받는다.
198+
5. GitHub 응답 `tree[]`에서 `type=blob``file`, `type=tree``dir`(경로는 디렉터리에 `/` 접미사)으로 매핑한다.
199+
6. `truncated=true`이면 레포가 너무 커서 GitHub이 트리를 잘랐을 때이며, 이 경우 **502**를 반환한다.
193200

194201
#### 1. Request Syntax
195202

196203
```bash
197-
curl -X GET "https://example.com/api/github/repos/123/files?path=/&depth=2&ref=main" \
204+
curl -X GET "https://example.com/api/github/repos/123/files?path=/&depth=-1&ref=main" \
198205
-H "Authorization: Bearer <app-session-token>"
199206
```
200207

@@ -210,9 +217,9 @@ curl -X GET "https://example.com/api/github/repos/123/files?path=/&depth=2&ref=m
210217
| 파라미터 | 타입 | 필수 | 설명 |
211218
|----------|------|------|------|
212219
| repo_id | integer \| string | Y | Path. GitHub 레포 numeric ID 또는 `"owner/name"` |
213-
| path | string | N | 조회 시작 디렉터리 경로, default="/" |
214-
| depth | integer | N | 탐색 최대 깊이, default=2 |
215-
| ref | string | N | 브랜치명 또는 커밋 SHA, default=레포 default_branch |
220+
| path | string | N | 조회 시작 디렉터리 경로, default="/" (해당 경로 하위만 필터) |
221+
| depth | integer | N | `path` 기준 상대 경로에서 `/` 개수 상한. `-1`이면 깊이 제한 없음 (default=-1) |
222+
| ref | string | N | 브랜치명 또는 커밋 SHA, 생략 시 레포 `default_branch` |
216223

217224
#### 4. Response
218225

@@ -228,7 +235,8 @@ curl -X GET "https://example.com/api/github/repos/123/files?path=/&depth=2&ref=m
228235
{ "path": "src/app/", "type": "dir" },
229236
{ "path": "src/app/main.py", "type": "file" },
230237
{ "path": "README.md", "type": "file" }
231-
]
238+
],
239+
"visited_nodes": 4
232240
}
233241
```
234242

@@ -238,7 +246,7 @@ curl -X GET "https://example.com/api/github/repos/123/files?path=/&depth=2&ref=m
238246
| 404 | NOT_FOUND | 레포 또는 경로 없음 |
239247

240248
> 공통 에러(400/401/403/404/500/502)는 공통 규칙 참고.
241-
| 502 | GITHUB_UPSTREAM_ERROR | GitHub 트리 조회 실패 |
249+
| 502 | GITHUB_UPSTREAM_ERROR | GitHub 트리 조회 실패 또는 GitHub `truncated=true` (트리 과대) |
242250

243251
---
244252

docs/openapi.json

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@
358358
"required": false,
359359
"schema": {
360360
"type": "integer",
361-
"default": 2,
361+
"default": -1,
362362
"title": "Depth"
363363
}
364364
},
@@ -607,6 +607,70 @@
607607
}
608608
}
609609
},
610+
"/api/user/selected-repo-assets": {
611+
"get": {
612+
"tags": [
613+
"UserAssets"
614+
],
615+
"summary": "Selected Repo Assets Get",
616+
"operationId": "selected_repo_assets_get_api_user_selected_repo_assets_get",
617+
"parameters": [
618+
{
619+
"name": "selected_repo_id",
620+
"in": "query",
621+
"required": false,
622+
"schema": {
623+
"anyOf": [
624+
{
625+
"type": "integer"
626+
},
627+
{
628+
"type": "null"
629+
}
630+
],
631+
"title": "Selected Repo Id"
632+
}
633+
}
634+
],
635+
"responses": {
636+
"200": {
637+
"description": "Successful Response",
638+
"content": {
639+
"application/json": {
640+
"schema": {}
641+
}
642+
}
643+
},
644+
"422": {
645+
"description": "Validation Error",
646+
"content": {
647+
"application/json": {
648+
"schema": {
649+
"$ref": "#/components/schemas/HTTPValidationError"
650+
}
651+
}
652+
}
653+
}
654+
}
655+
},
656+
"put": {
657+
"tags": [
658+
"UserAssets"
659+
],
660+
"summary": "Selected Repo Assets Put",
661+
"operationId": "selected_repo_assets_put_api_user_selected_repo_assets_put",
662+
"responses": {
663+
"200": {
664+
"description": "Successful Response",
665+
"content": {
666+
"application/json": {
667+
"schema": {}
668+
}
669+
}
670+
}
671+
}
672+
}
673+
},
610674
"/": {
611675
"get": {
612676
"summary": "Index",

src/api/github.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from __future__ import annotations
22

3-
import os
43
from datetime import datetime, timezone
5-
from typing import Any, Dict, List, Optional
4+
from typing import List
65

76
from fastapi import APIRouter, Request, Response
87
from fastapi.responses import JSONResponse, PlainTextResponse
98

109
from src.db.sqlite.client import connect
1110
from src.service.git_hub import repos as github_repos
11+
from src.service.git_hub.repos import GitHubTreeTruncatedError
1212
from src.service.user.repos import (
1313
get_selected_repos_detailed,
1414
upsert_selected_repos,
@@ -147,9 +147,8 @@ async def github_repo_files(
147147
request: Request,
148148
repo_id: str,
149149
path: str = "/",
150-
# depth=-1이면 “끝까지(단 traverse_cap까지)” 순회한다.
150+
# depth=-1이면 경로 깊이 필터 없음.
151151
depth: int = -1,
152-
traverse_cap: int = 500,
153152
ref: str | None = None,
154153
) -> JSONResponse:
155154
try:
@@ -167,15 +166,19 @@ async def github_repo_files(
167166
repo=repo,
168167
path=path,
169168
depth=depth,
170-
traverse_cap=traverse_cap,
171169
ref=ref,
172170
)
173171
# docs response에서 repo_id를 그대로 노출한다.
174172
result["repo_id"] = repo_id if repo_id else full_name
175-
result["ref"] = ref
176173
return JSONResponse(result)
177174
except ValueError:
178175
return _error_response(400, "BAD_REQUEST", "Invalid repo_id")
176+
except GitHubTreeTruncatedError as exc:
177+
return _error_response(
178+
502,
179+
"GITHUB_UPSTREAM_ERROR",
180+
f"GitHub tree truncated: {exc}",
181+
)
179182
except Exception as exc:
180183
return _error_response(
181184
502,

0 commit comments

Comments
 (0)