Skip to content

Commit 64d8e60

Browse files
committed
add /api/{user}/{repository} endpoint and return openapi.json on all available options on /api and /api/
1 parent 8e3195f commit 64d8e60

File tree

4 files changed

+123
-8
lines changed

4 files changed

+123
-8
lines changed

src/server/main.py

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from dotenv import load_dotenv
99
from fastapi import FastAPI, Request
10-
from fastapi.responses import FileResponse, HTMLResponse
10+
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
1111
from fastapi.staticfiles import StaticFiles
1212
from slowapi.errors import RateLimitExceeded
1313
from starlette.middleware.trustedhost import TrustedHostMiddleware
@@ -100,10 +100,52 @@ async def llm_txt() -> FileResponse:
100100

101101
@app.get("/docs", response_class=HTMLResponse)
102102
async def custom_swagger_ui(request: Request) -> HTMLResponse:
103-
"""Render the Swagger UI documentation page inside the Jinja theme."""
103+
"""Swagger UI documentation."""
104104
return templates.TemplateResponse("swagger_ui.jinja", {"request": request})
105105

106106

107+
@app.get("/api", include_in_schema=True)
108+
@app.get("/api/", include_in_schema=True)
109+
def openapi_json_get() -> JSONResponse:
110+
"""Return the OpenAPI schema (openapi.json)."""
111+
return JSONResponse(app.openapi())
112+
113+
114+
@app.post("/api", include_in_schema=False)
115+
@app.post("/api/", include_in_schema=True)
116+
def openapi_json_post() -> JSONResponse:
117+
"""Return the OpenAPI schema (openapi.json)."""
118+
return JSONResponse(app.openapi())
119+
120+
121+
@app.put("/api", include_in_schema=False)
122+
@app.put("/api/", include_in_schema=False)
123+
def openapi_json_put() -> JSONResponse:
124+
"""Return the OpenAPI schema (openapi.json)."""
125+
return JSONResponse(app.openapi())
126+
127+
128+
@app.delete("/api", include_in_schema=False)
129+
@app.delete("/api/", include_in_schema=False)
130+
def openapi_json_delete() -> JSONResponse:
131+
"""Return the OpenAPI schema (openapi.json)."""
132+
return JSONResponse(app.openapi())
133+
134+
135+
@app.options("/api", include_in_schema=False)
136+
@app.options("/api/", include_in_schema=False)
137+
def openapi_json_options() -> JSONResponse:
138+
"""Return the OpenAPI schema (openapi.json)."""
139+
return JSONResponse(app.openapi())
140+
141+
142+
@app.head("/api", include_in_schema=False)
143+
@app.head("/api/", include_in_schema=False)
144+
def openapi_json_head() -> JSONResponse:
145+
"""Return the OpenAPI schema (openapi.json)."""
146+
return JSONResponse(app.openapi())
147+
148+
107149
# Include routers for modular endpoints
108150
app.include_router(index)
109151
app.include_router(ingest)

src/server/models.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,10 @@ class IngestErrorResponse(BaseModel):
9999
----------
100100
error : str
101101
Error message describing what went wrong.
102-
repo_url : str
103-
The repository URL that failed to process.
104102
105103
"""
106104

107105
error: str = Field(..., description="Error message")
108-
repo_url: str = Field(..., description="Repository URL that failed")
109106

110107

111108
# Union type for API responses

src/server/query_processor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ async def process_query(
102102
print(f"{Colors.BROWN}WARN{Colors.END}: {Colors.RED}<- {Colors.END}", end="")
103103
print(f"{Colors.RED}{exc}{Colors.END}")
104104

105-
return IngestErrorResponse(error=str(exc), repo_url=short_repo_url)
105+
return IngestErrorResponse(error=str(exc))
106106

107107
if len(content) > MAX_DISPLAY_SIZE:
108108
content = (

src/server/routers/ingest.py

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from server.models import IngestErrorResponse, IngestRequest, IngestSuccessResponse
77
from server.query_processor import process_query
8+
from server.server_config import DEFAULT_MAX_FILE_SIZE_KB
89
from server.server_utils import limiter
910

1011
router = APIRouter()
@@ -64,7 +65,6 @@ async def api_ingest(
6465
# Handle validation errors with 400 status code
6566
error_response = IngestErrorResponse(
6667
error=f"Validation error: {ve!s}",
67-
repo_url=ingest_request.input_text,
6868
)
6969
return JSONResponse(
7070
status_code=status.HTTP_400_BAD_REQUEST,
@@ -75,7 +75,83 @@ async def api_ingest(
7575
# Handle unexpected errors with 500 status code
7676
error_response = IngestErrorResponse(
7777
error=f"Internal server error: {exc!s}",
78-
repo_url=ingest_request.input_text,
78+
)
79+
return JSONResponse(
80+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
81+
content=error_response.model_dump(),
82+
)
83+
84+
85+
@router.get(
86+
"/api/{user}/{repository}",
87+
responses={
88+
status.HTTP_200_OK: {"model": IngestSuccessResponse, "description": "Successful ingestion"},
89+
status.HTTP_400_BAD_REQUEST: {"model": IngestErrorResponse, "description": "Bad request or processing error"},
90+
status.HTTP_500_INTERNAL_SERVER_ERROR: {"model": IngestErrorResponse, "description": "Internal server error"},
91+
},
92+
)
93+
@limiter.limit("10/minute")
94+
async def api_ingest_get(
95+
request: Request, # noqa: ARG001
96+
user: str,
97+
repository: str,
98+
max_file_size: int = DEFAULT_MAX_FILE_SIZE_KB,
99+
pattern_type: str = "exclude",
100+
pattern: str = "",
101+
token: str = "",
102+
) -> JSONResponse:
103+
"""Ingest a GitHub repository via GET and return processed content.
104+
105+
**This endpoint processes a GitHub repository by analyzing its structure and returning a summary**
106+
with the repository's content. The response includes file tree structure, processed content, and
107+
metadata about the ingestion. All ingestion parameters are optional and can be provided as query parameters.
108+
109+
**Path Parameters**
110+
- **user** (`str`): GitHub username or organization
111+
- **repository** (`str`): GitHub repository name
112+
113+
**Query Parameters**
114+
- **max_file_size** (`int`, optional): Maximum file size to include in the digest (default: 50 KB)
115+
- **pattern_type** (`str`, optional): Type of pattern to use ("include" or "exclude", default: "exclude")
116+
- **pattern** (`str`, optional): Pattern to include or exclude in the query (default: "")
117+
- **token** (`str`, optional): GitHub personal access token for private repositories (default: "")
118+
119+
**Returns**
120+
- **JSONResponse**: Success response with ingestion results or error response with appropriate HTTP status code
121+
""" # pylint: disable=unused-argument
122+
try:
123+
effective_input_text = f"{user}/{repository}"
124+
result = await process_query(
125+
input_text=effective_input_text,
126+
slider_position=max_file_size,
127+
pattern_type=pattern_type,
128+
pattern=pattern,
129+
token=token or None,
130+
)
131+
132+
if isinstance(result, IngestErrorResponse):
133+
return JSONResponse(
134+
status_code=status.HTTP_400_BAD_REQUEST,
135+
content=result.model_dump(),
136+
)
137+
138+
return JSONResponse(
139+
status_code=status.HTTP_200_OK,
140+
content=result.model_dump(),
141+
)
142+
143+
except ValueError as ve:
144+
error_response = IngestErrorResponse(
145+
error=f"Validation error: {ve!s}",
146+
)
147+
return JSONResponse(
148+
status_code=status.HTTP_400_BAD_REQUEST,
149+
content=error_response.model_dump(),
150+
)
151+
152+
except Exception as exc:
153+
error_response = IngestErrorResponse(
154+
error=f"Internal server error: {exc!s}",
79155
)
80156
return JSONResponse(
81157
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,

0 commit comments

Comments
 (0)