-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmain.py
More file actions
155 lines (126 loc) · 5.36 KB
/
Copy pathmain.py
File metadata and controls
155 lines (126 loc) · 5.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""
Main entrypoint for the FastAPI server
"""
from fastapi import FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.routing import APIRoute
from core.lifespan import lifespan
from core.config import get_settings
from api.auth.routes import router as auth_router
from api.auth.oauth_routes import router as oauth_router
from api.actions.routes import router as actions_router
from api.files.routes import router as files_router
from api.jobs.routes import router as jobs_router
from api.manifest.routes import router as manifest_router
from api.project.routes import router as project_router
from api.qcmetrics.routes import router as qcmetrics_router
from api.runs.routes import router as runs_router
from api.samples.routes import router as samples_router
from api.search.routes import router as search_router
from api.settings.routes import router as settings_router
from api.vendors.routes import router as vendors_router
from api.workflow.routes import router as workflow_router
from api.pipeline.routes import router as pipeline_router
from api.platforms.routes import router as platforms_router
from api.users.routes import router as users_router
# Customize route id's
# Helpful for creating sensible names in the client
def custom_generate_unique_id(route: APIRoute):
""" Generate unique route IDs based on route name """
return f"{route.name}" # these must be unique
# Create schema & router
app = FastAPI(lifespan=lifespan, generate_unique_id_function=custom_generate_unique_id)
# Generic validation error handler
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
"""
Generic validation error handler that provides detailed, actionable error messages
for any endpoint with validation errors.
"""
errors = exc.errors()
# Build a user-friendly error response
formatted_errors = []
for error in errors:
# Get the field path (e.g., ['body', 'email'] -> 'email')
field_path = " -> ".join(str(loc) for loc in error["loc"] if loc != "body")
formatted_error = {
"field": field_path or "body",
"message": error["msg"],
"type": error["type"],
}
# Add input value if available (helps debugging)
if "input" in error:
input_val = error.get("input")
# Ensure the value is JSON-serializable (e.g., convert bytes to string)
if isinstance(input_val, bytes):
input_val = input_val.decode("utf-8", errors="replace")
formatted_error["received"] = input_val
formatted_errors.append(formatted_error)
# Determine if the entire body is missing
is_missing_body = any(
error["type"] == "missing" and "body" in error["loc"]
for error in errors
)
if is_missing_body:
message = "Request body is required but was not provided"
hint = f"Please send a JSON body with your request to {request.method} {request.url.path}"
else:
message = "Validation error in request"
hint = "Please check the errors below and correct your request"
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
content={
"detail": message,
"hint": hint,
"errors": formatted_errors,
"docs_url": f"{request.base_url}docs#{request.url.path.replace('/', '-').strip('-')}"
}
)
# CORS settings to allow client-server communication
# Set with env variable
origins = [get_settings().client_origin]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Create a simple health check endpoint
@app.get("/", tags=["index"])
def root():
return {"message": "Welcome to the NGS360 API! Visit /docs for API documentation."}
@app.get("/api/health", tags=["health"])
def health_check():
return {"status": "ok", "message": "NGS360 API is running."}
# REST routers
# Add each api/feature folder here
API_PREFIX = "/api/v1"
# Authentication routers (no auth required)
app.include_router(auth_router, prefix=API_PREFIX)
app.include_router(oauth_router, prefix=API_PREFIX)
# Feature routers
app.include_router(actions_router, prefix=API_PREFIX)
app.include_router(files_router, prefix=API_PREFIX)
app.include_router(jobs_router, prefix=API_PREFIX)
app.include_router(manifest_router, prefix=API_PREFIX)
app.include_router(project_router, prefix=API_PREFIX)
app.include_router(qcmetrics_router, prefix=API_PREFIX)
app.include_router(runs_router, prefix=API_PREFIX)
app.include_router(samples_router, prefix=API_PREFIX)
app.include_router(search_router, prefix=API_PREFIX)
app.include_router(settings_router, prefix=API_PREFIX)
app.include_router(vendors_router, prefix=API_PREFIX)
app.include_router(workflow_router, prefix=API_PREFIX)
app.include_router(pipeline_router, prefix=API_PREFIX)
app.include_router(platforms_router, prefix=API_PREFIX)
app.include_router(users_router, prefix=API_PREFIX)
if __name__ == "__main__":
# For debugging purposes
import uvicorn
import os
host = os.getenv("API_HOST", "127.0.0.1")
port = int(os.getenv("API_PORT", 8000))
uvicorn.run("main:app", host=host, port=port, reload=True)