From 6fa366fbf2359ce6329a6390989f46c1a531338e Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Thu, 17 Apr 2025 16:07:15 +0300 Subject: [PATCH] fix fastapi swagger --- .../bootstrappers/fastapi_bootstrapper.py | 13 ++++++++++++- .../bootstrappers/litestar_bootstrapper.py | 2 +- lite_bootstrap/instruments/swagger_instrument.py | 3 +-- tests/test_fastapi_bootstrap.py | 10 ++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py b/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py index d0d3083..8b897a3 100644 --- a/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py @@ -1,6 +1,7 @@ import contextlib import dataclasses import typing +import warnings from lite_bootstrap import import_checker from lite_bootstrap.bootstrappers.base import BaseBootstrapper @@ -35,12 +36,16 @@ class FastAPIConfig( CorsConfig, HealthChecksConfig, LoggingConfig, OpentelemetryConfig, PrometheusConfig, SentryConfig, SwaggerConfig ): - application: "fastapi.FastAPI" = dataclasses.field(default_factory=lambda: fastapi.FastAPI()) + application: "fastapi.FastAPI" = dataclasses.field(default=None) # type: ignore[assignment] opentelemetry_excluded_urls: list[str] = dataclasses.field(default_factory=list) prometheus_instrumentator_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) prometheus_instrument_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) prometheus_expose_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) + def __post_init__(self) -> None: + if not self.application: + object.__setattr__(self, "application", fastapi.FastAPI(docs_url=self.swagger_path)) + @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) class FastApiCorsInstrument(CorsInstrument): @@ -139,6 +144,12 @@ class FastApiSwaggerInstrument(SwaggerInstrument): bootstrap_config: FastAPIConfig def bootstrap(self) -> None: + if self.bootstrap_config.swagger_path != self.bootstrap_config.application.docs_url: + warnings.warn( + f"swagger_path is differ from docs_url, " + f"{self.bootstrap_config.application.docs_url} will be used for docs path", + stacklevel=2, + ) if self.bootstrap_config.swagger_offline_docs: enable_offline_docs( self.bootstrap_config.application, static_path=self.bootstrap_config.swagger_static_path diff --git a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py index 6d7bb4b..99bf29f 100644 --- a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py @@ -49,7 +49,7 @@ class LitestarConfig( application_config: "AppConfig" = dataclasses.field(default_factory=lambda: AppConfig()) opentelemetry_excluded_urls: list[str] = dataclasses.field(default_factory=list) prometheus_additional_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) - swagger_path: str = "/docs" + swagger_extra_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) diff --git a/lite_bootstrap/instruments/swagger_instrument.py b/lite_bootstrap/instruments/swagger_instrument.py index ed415c7..68e54de 100644 --- a/lite_bootstrap/instruments/swagger_instrument.py +++ b/lite_bootstrap/instruments/swagger_instrument.py @@ -1,5 +1,4 @@ import dataclasses -import typing from lite_bootstrap.instruments.base import BaseConfig, BaseInstrument @@ -7,8 +6,8 @@ @dataclasses.dataclass(kw_only=True, frozen=True) class SwaggerConfig(BaseConfig): swagger_static_path: str = "/static" + swagger_path: str = "/docs" swagger_offline_docs: bool = False - swagger_extra_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) diff --git a/tests/test_fastapi_bootstrap.py b/tests/test_fastapi_bootstrap.py index d1a61f4..a3e8685 100644 --- a/tests/test_fastapi_bootstrap.py +++ b/tests/test_fastapi_bootstrap.py @@ -1,3 +1,6 @@ +import dataclasses + +import fastapi import pytest import structlog from opentelemetry.sdk.trace.export import ConsoleSpanExporter @@ -61,6 +64,13 @@ def test_fastapi_bootstrapper_not_ready() -> None: FastAPIBootstrapper(bootstrap_config=FastAPIConfig()) +def test_fastapi_bootstrapper_docs_url_differ(fastapi_config: FastAPIConfig) -> None: + new_config = dataclasses.replace(fastapi_config, application=fastapi.FastAPI(docs_url="/custom-docs/")) + bootstrapper = FastAPIBootstrapper(bootstrap_config=new_config) + with pytest.warns(UserWarning, match="swagger_path is differ from docs_url"): + bootstrapper.bootstrap() + + @pytest.mark.parametrize( "package_name", [