Coverage for apps/kwai-api/src/kwai_api/app.py: 81%
74 statements
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2024-01-01 00:00 +0000
1"""Module that implements a factory method for a FastAPI application."""
3import os
4import sys
5import uuid
7from contextlib import asynccontextmanager
9from fastapi import FastAPI, Request, status
10from fastapi.middleware.cors import CORSMiddleware
11from fastapi.responses import JSONResponse
12from kwai_core.settings import LoggerSettings, Settings, get_settings
13from loguru import logger
15from kwai_api.frontend.app import create_frontend
16from kwai_api.v1.auth.api import api_router as auth_api_router
17from kwai_api.v1.club.api import api_router as club_api_router
18from kwai_api.v1.news.api import api_router as news_api_router
19from kwai_api.v1.pages.api import api_router as pages_api_router
20from kwai_api.v1.portal.api import api_router as portal_api_router
21from kwai_api.v1.teams.api import router as teams_api_router
22from kwai_api.v1.trainings.api import api_router as training_api_router
25APP_NAME = "kwai API"
28@asynccontextmanager
29async def lifespan(app: FastAPI):
30 """Log the start/stop of the application."""
31 logger.info(f"{APP_NAME} is starting")
32 yield
33 logger.warning(f"{APP_NAME} has ended!")
36def configure_logger(settings: LoggerSettings):
37 """Configure the logger."""
38 try:
39 logger.remove(0) # Remove the default logger
40 except ValueError:
41 pass
43 def log_format(record):
44 """Change the format when a request_id is set in extra."""
45 if "request_id" in record["extra"]:
46 new_format = (
47 "{time} - {level} - ({extra[request_id]}) - {message}" + os.linesep
48 )
49 else:
50 new_format = "{time} - {level} - {message}" + os.linesep
51 if record["exception"]:
52 new_format += "{exception}" + os.linesep
53 return new_format
55 logger.add(
56 settings.file or sys.stderr,
57 format=log_format,
58 level=settings.level,
59 colorize=True,
60 retention=settings.retention,
61 rotation=settings.rotation,
62 backtrace=False,
63 diagnose=False,
64 )
67def create_api(settings: Settings | None = None) -> FastAPI:
68 """Create the FastAPI application.
70 Args:
71 settings: Settings to use in this application.
72 """
73 settings = settings or get_settings()
75 app = FastAPI(
76 lifespan=lifespan,
77 separate_input_output_schemas=False,
78 openapi_url=settings.openapi_url,
79 )
81 @app.middleware("http")
82 async def log(request: Request, call_next):
83 """Middleware for logging the requests."""
84 request_id = str(uuid.uuid4())
85 with logger.contextualize(request_id=request_id):
86 logger.info(f"{request.url} - {request.method} - Request started")
88 response = None # Make pylint happy...
89 try:
90 response = await call_next(request)
91 except Exception as ex:
92 logger.error(f"{request.url} - Request failed: {ex}")
93 logger.exception(ex)
94 response = JSONResponse(
95 content={
96 "detail": str(ex),
97 },
98 status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
99 )
100 finally:
101 if response:
102 response.headers["X-Request-ID"] = request_id
103 logger.info(
104 f"{request.url} - {request.method} - Request ended: "
105 f"{response.status_code}"
106 )
108 return response
110 # Setup CORS
111 if settings.cors:
112 app.add_middleware(
113 CORSMiddleware,
114 allow_origins=settings.cors.origins,
115 allow_credentials=True,
116 allow_methods=settings.cors.methods,
117 allow_headers=settings.cors.headers,
118 )
120 # Setup the logger.
121 if settings.logger:
122 configure_logger(settings.logger)
124 app.include_router(auth_api_router, prefix="/v1")
125 app.include_router(portal_api_router, prefix="/v1")
126 app.include_router(pages_api_router, prefix="/v1")
127 app.include_router(news_api_router, prefix="/v1")
128 app.include_router(teams_api_router, prefix="/v1")
129 app.include_router(training_api_router, prefix="/v1")
130 app.include_router(club_api_router, prefix="/v1")
132 return app
135def create_app() -> FastAPI:
136 """Create the FastAPI application for API and frontend."""
137 main_app = FastAPI(title=APP_NAME, lifespan=lifespan)
139 api_app = create_api()
140 main_app.mount("/api", api_app)
142 frontend_app = create_frontend()
143 main_app.mount("/", frontend_app)
145 return main_app