|
1 | 1 | import json |
2 | 2 | import logging |
3 | 3 | import os |
4 | | -from collections.abc import AsyncIterator |
| 4 | +from collections.abc import AsyncIterator, Iterator |
5 | 5 | from contextlib import asynccontextmanager |
6 | 6 | from pathlib import Path |
7 | 7 | from typing import Any |
|
13 | 13 | from gfmodules.logging.middleware import RequestContextMiddleware |
14 | 14 |
|
15 | 15 | from app import container |
16 | | -from app.auth import get_auth_ctx |
| 16 | +from app.auth import auth_log_context, get_auth_ctx |
17 | 17 | from app.config import ( |
18 | 18 | _ENVIRONMENT_CONFIG_PATH_NAME, |
19 | 19 | _PATH, |
20 | 20 | get_config, |
21 | 21 | ) |
22 | 22 | from app.errors.handlers import register_exceptions |
| 23 | +from app.logging.context import CLAIM_FIELDS |
23 | 24 | from app.logging.events import Log |
| 25 | +from app.models.fhir.resources.localization_list.request import COUNT_PARAM |
24 | 26 | from app.routers.default import router as default_router |
25 | 27 | from app.routers.fhir.base import router as fhir_base_router |
26 | 28 | from app.routers.fhir.localization_list import router as fhir_list_router |
27 | 29 | from app.routers.health import router as health_router |
28 | 30 | from app.routers.localize import router as localization_router |
29 | 31 | from app.routers.registrations import router as registrations_router |
30 | | -from app.stats import StatsdMiddleware |
31 | 32 |
|
32 | 33 | logger = logging.getLogger(__name__) |
33 | 34 |
|
@@ -83,7 +84,12 @@ def create_fastapi_app() -> FastAPI: |
83 | 84 |
|
84 | 85 | def setup_logging() -> None: |
85 | 86 | config = get_config() |
86 | | - gflog.configure(config=config.logging, loglevel=config.app.loglevel, catalogue=Log) |
| 87 | + gflog.configure( |
| 88 | + config=config.logging, |
| 89 | + loglevel=config.app.loglevel, |
| 90 | + catalogue=Log, |
| 91 | + extra_context_fields=CLAIM_FIELDS, |
| 92 | + ) |
87 | 93 |
|
88 | 94 |
|
89 | 95 | def _read_version() -> str: |
@@ -123,6 +129,42 @@ def api_key_headers(document_gf_headers: bool) -> list[Any]: |
123 | 129 | return [Security(APIKeyHeader(name=header, scheme_name=header, auto_error=False)) for header in headers] |
124 | 130 |
|
125 | 131 |
|
| 132 | +def _count_params(schema: dict[str, Any]) -> Iterator[dict[str, Any]]: |
| 133 | + for operations in schema.get("paths", {}).values(): |
| 134 | + for operation in operations.values(): |
| 135 | + yield from ( |
| 136 | + param |
| 137 | + for param in operation.get("parameters", []) |
| 138 | + if param.get("in") == "query" and param.get("name") == COUNT_PARAM |
| 139 | + ) |
| 140 | + |
| 141 | + |
| 142 | +def _set_page_sizes(param: dict[str, Any], default_page_size: int, max_page_size: int) -> None: |
| 143 | + param["description"] = ( |
| 144 | + f"Number of entries per page. Defaults to {default_page_size}, larger values are capped at {max_page_size}" |
| 145 | + ) |
| 146 | + param["schema"]["default"] = default_page_size |
| 147 | + for option in param["schema"].get("anyOf", [param["schema"]]): |
| 148 | + if option.get("type") == "integer": |
| 149 | + option["maximum"] = max_page_size |
| 150 | + |
| 151 | + |
| 152 | +def _document_page_sizes(app: FastAPI, default_page_size: int, max_page_size: int) -> None: |
| 153 | + """ |
| 154 | + Shows the configured page sizes on the `_count` parameter in the OpenAPI schema. The maximum is not |
| 155 | + validated on the parameter itself: FHIR treats `_count` as a hint, so a larger value is capped instead of rejected. |
| 156 | + """ |
| 157 | + generate = app.openapi |
| 158 | + |
| 159 | + def openapi() -> dict[str, Any]: |
| 160 | + if app.openapi_schema is None: |
| 161 | + for param in _count_params(generate()): |
| 162 | + _set_page_sizes(param, default_page_size, max_page_size) |
| 163 | + return app.openapi_schema # type: ignore[return-value] |
| 164 | + |
| 165 | + app.openapi = openapi # type: ignore[method-assign] |
| 166 | + |
| 167 | + |
126 | 168 | def setup_fastapi() -> FastAPI: |
127 | 169 | config = get_config() |
128 | 170 | fastapi = ( |
@@ -152,12 +194,10 @@ def setup_fastapi() -> FastAPI: |
152 | 194 | fastapi.include_router(router) |
153 | 195 |
|
154 | 196 | for router in routers: |
155 | | - fastapi.include_router(router, dependencies=[Depends(get_auth_ctx)]) |
| 197 | + fastapi.include_router(router, dependencies=[Depends(get_auth_ctx), Depends(auth_log_context)]) |
156 | 198 |
|
157 | 199 | register_exceptions(fastapi) |
158 | | - |
159 | | - if config.stats.enabled: |
160 | | - fastapi.add_middleware(StatsdMiddleware, module_name=config.stats.module_name or "default") |
| 200 | + _document_page_sizes(fastapi, config.pagination.default_page_size, config.pagination.max_page_size) |
161 | 201 |
|
162 | 202 | fastapi.add_middleware( |
163 | 203 | RequestContextMiddleware, |
|
0 commit comments