Skip to content

Commit ad831e2

Browse files
committed
feat: add AlphaFold version proxy endpoint
- New /api/v1/alphafold-files/version endpoint (CORS-safe) - Fetches upstream XML index, handles optional namespace - Extracts model version from NextMarker (model_vN) and returns {"version": "v6"} (lowercased) - Error responses: upstream fetch failure (status passthrough), malformed ID (500), XML parse error (502)
1 parent 6419b01 commit ad831e2

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

src/mavedb/routers/alphafold.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from fastapi import APIRouter, HTTPException, Query
2+
from fastapi.responses import StreamingResponse
3+
import httpx
4+
from urllib.parse import quote
5+
import xml.etree.ElementTree as ET
6+
import re
7+
8+
from mavedb.lib.logging.logged_route import LoggedRoute
9+
10+
ALPHAFOLD_BASE = "https://alphafold.ebi.ac.uk/files/"
11+
12+
router = APIRouter(
13+
prefix="/api/v1",
14+
tags=["alphafold files"],
15+
responses={404: {"description": "Not found"}},
16+
route_class=LoggedRoute,
17+
)
18+
19+
@router.get("/alphafold-files/version")
20+
async def proxy_alphafold_index():
21+
"""
22+
Proxy the AlphaFold files index (XML document).
23+
"""
24+
async with httpx.AsyncClient(follow_redirects=True, timeout=30) as client:
25+
resp = await client.get(ALPHAFOLD_BASE, headers={"Accept": "application/xml"})
26+
if resp.status_code != 200:
27+
raise HTTPException(status_code=resp.status_code, detail="Upstream error fetching AlphaFold files index")
28+
29+
# parse XML response
30+
try:
31+
root = ET.fromstring(resp.content)
32+
33+
# Detect default namespace
34+
if root.tag.startswith("{"):
35+
ns_uri = root.tag.split("}", 1)[0][1:]
36+
ns = {"x": ns_uri}
37+
next_marker_tag = "x:NextMarker"
38+
else:
39+
ns = {}
40+
next_marker_tag = "NextMarker"
41+
42+
next_marker_el = root.find(next_marker_tag, ns)
43+
next_marker = next_marker_el.text if next_marker_el is not None else None
44+
45+
match = re.search(r"model_(v\d+)\.pdb$", next_marker, re.IGNORECASE)
46+
if not match:
47+
raise HTTPException(status_code=500, detail="Malformed AlphaFold PDB ID in XML")
48+
version = match.group(1)
49+
return {"version": version.lower()}
50+
51+
except ET.ParseError as e:
52+
raise HTTPException(status_code=502, detail=f"Failed to parse upstream XML: {e}")

src/mavedb/server_main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
taxonomies,
5959
users,
6060
variants,
61+
alphafold,
6162
)
6263

6364
logger = logging.getLogger(__name__)
@@ -106,6 +107,7 @@
106107
app.include_router(taxonomies.router)
107108
app.include_router(users.router)
108109
app.include_router(variants.router)
110+
app.include_router(alphafold.router)
109111

110112

111113
@app.exception_handler(PermissionException)

0 commit comments

Comments
 (0)