|
6 | 6 | """ |
7 | 7 |
|
8 | 8 | import logging |
9 | | -from contextlib import asynccontextmanager |
10 | | -from typing import Optional |
| 9 | +from typing import Any, Optional |
11 | 10 |
|
12 | 11 | from fastapi import FastAPI |
13 | 12 | from starlette_cramjam.middleware import CompressionMiddleware |
14 | 13 |
|
15 | 14 | from .config import Settings |
16 | 15 | from .handlers import HealthzHandler, ReverseProxyHandler, SwaggerUI |
| 16 | +from .lifespan import build_lifespan |
17 | 17 | from .middleware import ( |
18 | 18 | AddProcessTimeHeaderMiddleware, |
19 | 19 | AuthenticationExtensionMiddleware, |
|
26 | 26 | ProcessLinksMiddleware, |
27 | 27 | RemoveRootPathMiddleware, |
28 | 28 | ) |
29 | | -from .utils.lifespan import check_conformance, check_server_health |
30 | 29 |
|
31 | 30 | logger = logging.getLogger(__name__) |
32 | 31 |
|
33 | 32 |
|
34 | | -def create_app(settings: Optional[Settings] = None) -> FastAPI: |
35 | | - """FastAPI Application Factory.""" |
36 | | - settings = settings or Settings() |
| 33 | +def configure_app( |
| 34 | + app: FastAPI, |
| 35 | + settings: Optional[Settings] = None, |
| 36 | + **settings_kwargs: Any, |
| 37 | +) -> FastAPI: |
| 38 | + """ |
| 39 | + Apply routes and middleware to a FastAPI app. |
| 40 | +
|
| 41 | + Parameters |
| 42 | + ---------- |
| 43 | + app : FastAPI |
| 44 | + The FastAPI app to configure. |
| 45 | + settings : Settings | None, optional |
| 46 | + Pre-built settings instance. If omitted, a new one is constructed from |
| 47 | + ``settings_kwargs``. |
| 48 | + **settings_kwargs : Any |
| 49 | + Keyword arguments used to configure the health and conformance checks if |
| 50 | + ``settings`` is not provided. |
| 51 | + """ |
| 52 | + settings = settings or Settings(**settings_kwargs) |
37 | 53 |
|
38 | 54 | # |
39 | | - # Application |
40 | | - # |
41 | | - |
42 | | - @asynccontextmanager |
43 | | - async def lifespan(app: FastAPI): |
44 | | - assert settings |
45 | | - |
46 | | - # Wait for upstream servers to become available |
47 | | - if settings.wait_for_upstream: |
48 | | - logger.info("Running upstream server health checks...") |
49 | | - urls = [settings.upstream_url, settings.oidc_discovery_internal_url] |
50 | | - for url in urls: |
51 | | - await check_server_health(url=url) |
52 | | - logger.info( |
53 | | - "Upstream servers are healthy:\n%s", |
54 | | - "\n".join([f" - {url}" for url in urls]), |
55 | | - ) |
56 | | - |
57 | | - # Log all middleware connected to the app |
58 | | - logger.info( |
59 | | - "Connected middleware:\n%s", |
60 | | - "\n".join([f" - {m.cls.__name__}" for m in app.user_middleware]), |
61 | | - ) |
62 | | - |
63 | | - if settings.check_conformance: |
64 | | - await check_conformance( |
65 | | - app.user_middleware, |
66 | | - str(settings.upstream_url), |
67 | | - ) |
68 | | - |
69 | | - yield |
70 | | - |
71 | | - app = FastAPI( |
72 | | - openapi_url=None, # Disable OpenAPI schema endpoint, we want to serve upstream's schema |
73 | | - lifespan=lifespan, |
74 | | - root_path=settings.root_path, |
75 | | - ) |
76 | | - if app.root_path: |
77 | | - logger.debug("Mounted app at %s", app.root_path) |
78 | | - |
79 | | - # |
80 | | - # Handlers (place catch-all proxy handler last) |
| 55 | + # Route Handlers |
81 | 56 | # |
82 | 57 |
|
83 | 58 | # If we have customized Swagger UI Init settings (e.g. a provided client_id) |
@@ -105,15 +80,6 @@ async def lifespan(app: FastAPI): |
105 | 80 | prefix=settings.healthz_prefix, |
106 | 81 | ) |
107 | 82 |
|
108 | | - app.add_api_route( |
109 | | - "/{path:path}", |
110 | | - ReverseProxyHandler( |
111 | | - upstream=str(settings.upstream_url), |
112 | | - override_host=settings.override_host, |
113 | | - ).proxy_request, |
114 | | - methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], |
115 | | - ) |
116 | | - |
117 | 83 | # |
118 | 84 | # Middleware (order is important, last added = first to run) |
119 | 85 | # |
@@ -186,3 +152,29 @@ async def lifespan(app: FastAPI): |
186 | 152 | ) |
187 | 153 |
|
188 | 154 | return app |
| 155 | + |
| 156 | + |
| 157 | +def create_app(settings: Optional[Settings] = None) -> FastAPI: |
| 158 | + """FastAPI Application Factory.""" |
| 159 | + settings = settings or Settings() |
| 160 | + |
| 161 | + app = FastAPI( |
| 162 | + openapi_url=None, # Disable OpenAPI schema endpoint, we want to serve upstream's schema |
| 163 | + lifespan=build_lifespan(settings=settings), |
| 164 | + root_path=settings.root_path, |
| 165 | + ) |
| 166 | + if app.root_path: |
| 167 | + logger.debug("Mounted app at %s", app.root_path) |
| 168 | + |
| 169 | + configure_app(app, settings) |
| 170 | + |
| 171 | + app.add_api_route( |
| 172 | + "/{path:path}", |
| 173 | + ReverseProxyHandler( |
| 174 | + upstream=str(settings.upstream_url), |
| 175 | + override_host=settings.override_host, |
| 176 | + ).proxy_request, |
| 177 | + methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], |
| 178 | + ) |
| 179 | + |
| 180 | + return app |
0 commit comments