1
+ from typing import Optional
1
2
import uuid
2
3
import time
3
4
import json
4
- from fastapi import APIRouter , Request , Depends , HTTPException
5
+ import tempfile
6
+ from fastapi import APIRouter , Request , Depends , HTTPException , UploadFile , File , Form
5
7
from folding_api .schemas import EpistulaHeaders
6
8
from folding_api .schemas import FoldingParams
7
9
from folding .utils .logging import logger
10
12
router = APIRouter ()
11
13
12
14
13
- @router .post ("/organic" )
14
- async def organic (
15
+ def verify_organic_request (
15
16
request : Request ,
16
17
job : FoldingParams ,
17
- epistula_headers : EpistulaHeaders = Depends ( EpistulaHeaders ) ,
18
- ):
18
+ epistula_headers : EpistulaHeaders ,
19
+ ) -> None :
19
20
"""
20
- This endpoint is used to receive organic requests. Returns success message with the job id.
21
- Args:
22
- request: Request
23
- job: FoldingParams
24
- epistula_headers: EpistulaHeaders
25
- Returns:
26
- dict[str, str]: dict with the job id.
21
+ Verify the organic request signature and whitelist.
27
22
"""
28
-
29
23
body_bytes = json .dumps (job .model_dump (), default = str , sort_keys = True ).encode (
30
24
"utf-8"
31
25
)
@@ -37,7 +31,6 @@ async def organic(
37
31
raise HTTPException (status_code = 403 , detail = str (e ))
38
32
39
33
sender_hotkey = epistula_headers .signed_by
40
-
41
34
if sender_hotkey not in request .app .state .config .organic_whitelist :
42
35
logger .warning (
43
36
f"Received organic request from { sender_hotkey } , but { sender_hotkey } is not in the whitelist."
@@ -46,9 +39,87 @@ async def organic(
46
39
status_code = 403 , detail = "Forbidden, sender not in whitelist."
47
40
)
48
41
42
+
43
+ def get_folding_params (query : str = Form (...)) -> FoldingParams :
44
+ """
45
+ Dependency function to parse and validate the query form data.
46
+ """
47
+ try :
48
+ query_data = json .loads (query )
49
+ return FoldingParams (** query_data )
50
+ except Exception as e :
51
+ raise HTTPException (status_code = 400 , detail = f"Invalid query data: { str (e )} " )
52
+
53
+
54
+ @router .post ("/organic" )
55
+ async def organic (
56
+ request : Request ,
57
+ job : FoldingParams ,
58
+ epistula_headers : EpistulaHeaders = Depends (EpistulaHeaders ),
59
+ ):
60
+ """
61
+ This endpoint is used to receive organic requests for proteins from RCSB or PDBE databases.
62
+ Returns success message with the job id.
63
+
64
+ Args:
65
+ request: Request
66
+ job: FoldingParams
67
+ epistula_headers: EpistulaHeaders
68
+ Returns:
69
+ dict[str, str]: dict with the job id.
70
+ """
71
+ verify_organic_request (request , job , epistula_headers )
72
+
49
73
folding_params = job .model_dump ()
50
74
folding_params ["job_id" ] = str (uuid .uuid4 ())
75
+
51
76
logger .info (f"Received organic request: { folding_params } " )
52
77
request .app .state .validator ._organic_queue .add (folding_params )
53
78
54
79
return {"job_id" : folding_params ["job_id" ]}
80
+
81
+
82
+ @router .post ("/organic/upload" )
83
+ async def organic_with_upload (
84
+ request : Request ,
85
+ job : FoldingParams = Depends (get_folding_params ),
86
+ pdb_file : UploadFile = File (...),
87
+ epistula_headers : EpistulaHeaders = Depends (EpistulaHeaders ),
88
+ ):
89
+ """
90
+ This endpoint is used to receive organic requests with custom PDB files.
91
+ Returns success message with the job id.
92
+
93
+ Args:
94
+ request: Request
95
+ job: FoldingParams
96
+ pdb_file: PDB file upload
97
+ epistula_headers: EpistulaHeaders
98
+ Returns:
99
+ dict[str, str]: dict with the job id.
100
+ """
101
+ verify_organic_request (request , job , epistula_headers )
102
+
103
+ folding_params = job .model_dump ()
104
+ folding_params ["job_id" ] = str (uuid .uuid4 ())
105
+
106
+ # Handle PDB file
107
+ try :
108
+ # Create a temporary file to store the PDB
109
+ with tempfile .NamedTemporaryFile (
110
+ mode = "wb" , suffix = ".pdb" , delete = False
111
+ ) as temp_pdb :
112
+ content = await pdb_file .read ()
113
+ temp_pdb .write (content )
114
+ temp_pdb_path = temp_pdb .name
115
+
116
+ # Update folding params with the temporary file path
117
+ folding_params ["pdb_file_path" ] = temp_pdb_path
118
+ logger .info (f"Created temporary PDB file at: { temp_pdb_path } " )
119
+ except Exception as e :
120
+ raise HTTPException (status_code = 400 , detail = f"Invalid PDB file: { str (e )} " )
121
+
122
+ logger .info (f"Received organic request with PDB file: { folding_params } " )
123
+ request .app .state .validator ._organic_queue .add (folding_params )
124
+
125
+ return {"job_id" : folding_params ["job_id" ]}
0 commit comments