diff --git a/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py b/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py index 6ae762f..d0d3083 100644 --- a/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py @@ -4,7 +4,7 @@ from lite_bootstrap import import_checker from lite_bootstrap.bootstrappers.base import BaseBootstrapper -from lite_bootstrap.fastapi_offline_docs.main import enable_offline_docs +from lite_bootstrap.helpers.fastapi_helpers import enable_offline_docs from lite_bootstrap.instruments.cors_instrument import CorsConfig, CorsInstrument from lite_bootstrap.instruments.healthchecks_instrument import ( HealthChecksConfig, @@ -139,10 +139,9 @@ class FastApiSwaggerInstrument(SwaggerInstrument): bootstrap_config: FastAPIConfig def bootstrap(self) -> None: - self.bootstrap_config.application.docs_url = self.bootstrap_config.swagger_path if self.bootstrap_config.swagger_offline_docs: enable_offline_docs( - self.bootstrap_config.application, static_files_handler=self.bootstrap_config.service_static_path + 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 07b06e9..6d7bb4b 100644 --- a/lite_bootstrap/bootstrappers/litestar_bootstrapper.py +++ b/lite_bootstrap/bootstrappers/litestar_bootstrapper.py @@ -1,9 +1,9 @@ import dataclasses -import pathlib import typing from lite_bootstrap import import_checker from lite_bootstrap.bootstrappers.base import BaseBootstrapper +from lite_bootstrap.helpers.path import is_valid_path from lite_bootstrap.instruments.cors_instrument import CorsConfig, CorsInstrument from lite_bootstrap.instruments.healthchecks_instrument import ( HealthChecksConfig, @@ -49,6 +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" @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) @@ -145,15 +146,19 @@ class LitestarPrometheusController(PrometheusController): @dataclasses.dataclass(kw_only=True, frozen=True) class LitestarSwaggerInstrument(SwaggerInstrument): bootstrap_config: LitestarConfig + not_ready_message = "swagger_path is empty or not valid" + + def is_ready(self) -> bool: + return bool(self.bootstrap_config.swagger_path) and is_valid_path(self.bootstrap_config.swagger_path) def bootstrap(self) -> None: render_plugins: typing.Final = ( ( SwaggerRenderPlugin( - js_url=f"{self.bootstrap_config.service_static_path}/swagger-ui-bundle.js", - css_url=f"{self.bootstrap_config.service_static_path}/swagger-ui.css", + js_url=f"{self.bootstrap_config.swagger_static_path}/swagger-ui-bundle.js", + css_url=f"{self.bootstrap_config.swagger_static_path}/swagger-ui.css", standalone_preset_js_url=( - f"{self.bootstrap_config.service_static_path}/swagger-ui-standalone-preset.js" + f"{self.bootstrap_config.swagger_static_path}/swagger-ui-standalone-preset.js" ), ), ) @@ -169,10 +174,9 @@ def bootstrap(self) -> None: **self.bootstrap_config.swagger_extra_params, ) if self.bootstrap_config.swagger_offline_docs: - static_dir_path = pathlib.Path(__file__).parent.parent / "litestar_swagger_static" self.bootstrap_config.application_config.route_handlers.append( create_static_files_router( - path=self.bootstrap_config.service_static_path, directories=[static_dir_path] + path=self.bootstrap_config.swagger_static_path, directories=["lite_bootstrap/static/litestar_docs"] ) ) diff --git a/lite_bootstrap/fastapi_offline_docs/__init__.py b/lite_bootstrap/helpers/__init__.py similarity index 100% rename from lite_bootstrap/fastapi_offline_docs/__init__.py rename to lite_bootstrap/helpers/__init__.py diff --git a/lite_bootstrap/fastapi_offline_docs/main.py b/lite_bootstrap/helpers/fastapi_helpers.py similarity index 60% rename from lite_bootstrap/fastapi_offline_docs/main.py rename to lite_bootstrap/helpers/fastapi_helpers.py index a6790f3..377e27f 100644 --- a/lite_bootstrap/fastapi_offline_docs/main.py +++ b/lite_bootstrap/helpers/fastapi_helpers.py @@ -1,5 +1,3 @@ -import os -import pathlib import typing from lite_bootstrap import import_checker @@ -15,9 +13,7 @@ def enable_offline_docs( app: "FastAPI", - static_files_handler: str = "/static", - static_dir_path: os.PathLike[str] = pathlib.Path(__file__).parent / "static", - include_docs_in_schema: bool = False, + static_path: str, ) -> None: if not (app_openapi_url := app.openapi_url): msg = "No app.openapi_url specified" @@ -33,29 +29,27 @@ def enable_offline_docs( if typing.cast(Route, route).path not in (docs_url, redoc_url, swagger_ui_oauth2_redirect_url) ] - app.mount(static_files_handler, StaticFiles(directory=static_dir_path), name=static_files_handler) + app.mount(static_path, StaticFiles(directory="lite_bootstrap/static/fastapi_docs"), name="static") - @app.get(docs_url, include_in_schema=include_docs_in_schema) + @app.get(docs_url, include_in_schema=False) async def custom_swagger_ui_html(request: Request) -> HTMLResponse: - root_path = typing.cast(str, request.scope.get("root_path", "").rstrip("/")) - swagger_js_url = f"{root_path}{static_files_handler}/swagger-ui-bundle.js" - swagger_css_url = f"{root_path}{static_files_handler}/swagger-ui.css" + root_path = request.scope.get("root_path", "").rstrip("/") return get_swagger_ui_html( - openapi_url=root_path + app_openapi_url, + openapi_url=f"{root_path}{app_openapi_url}", title=f"{app.title} - Swagger UI", oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url, - swagger_js_url=swagger_js_url, - swagger_css_url=swagger_css_url, + swagger_js_url=f"{root_path}{static_path}/swagger-ui-bundle.js", + swagger_css_url=f"{root_path}{static_path}/swagger-ui.css", ) - @app.get(swagger_ui_oauth2_redirect_url, include_in_schema=include_docs_in_schema) + @app.get(swagger_ui_oauth2_redirect_url, include_in_schema=False) async def swagger_ui_redirect() -> HTMLResponse: return get_swagger_ui_oauth2_redirect_html() - @app.get(redoc_url, include_in_schema=include_docs_in_schema) + @app.get(redoc_url, include_in_schema=False) async def redoc_html() -> HTMLResponse: return get_redoc_html( openapi_url=app_openapi_url, title=f"{app.title} - ReDoc", - redoc_js_url=f"{static_files_handler}/redoc.standalone.js", + redoc_js_url=f"{static_path}/redoc.standalone.js", ) diff --git a/lite_bootstrap/helpers.py b/lite_bootstrap/helpers/path.py similarity index 100% rename from lite_bootstrap/helpers.py rename to lite_bootstrap/helpers/path.py diff --git a/lite_bootstrap/instruments/base.py b/lite_bootstrap/instruments/base.py index 59f3685..c1b4d9e 100644 --- a/lite_bootstrap/instruments/base.py +++ b/lite_bootstrap/instruments/base.py @@ -14,18 +14,15 @@ class BaseConfig: @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) class BaseInstrument(abc.ABC): bootstrap_config: BaseConfig + not_ready_message = "" missing_dependency_message = "" - @property - @abc.abstractmethod - def not_ready_message(self) -> str: ... - def bootstrap(self) -> None: ... # noqa: B027 def teardown(self) -> None: ... # noqa: B027 - @abc.abstractmethod - def is_ready(self) -> bool: ... + def is_ready(self) -> bool: + return True @staticmethod def check_dependencies() -> bool: diff --git a/lite_bootstrap/instruments/prometheus_instrument.py b/lite_bootstrap/instruments/prometheus_instrument.py index c45079f..f0a7897 100644 --- a/lite_bootstrap/instruments/prometheus_instrument.py +++ b/lite_bootstrap/instruments/prometheus_instrument.py @@ -1,6 +1,6 @@ import dataclasses -from lite_bootstrap.helpers import is_valid_path +from lite_bootstrap.helpers.path import is_valid_path from lite_bootstrap.instruments.base import BaseConfig, BaseInstrument diff --git a/lite_bootstrap/instruments/swagger_instrument.py b/lite_bootstrap/instruments/swagger_instrument.py index 780b76c..ed415c7 100644 --- a/lite_bootstrap/instruments/swagger_instrument.py +++ b/lite_bootstrap/instruments/swagger_instrument.py @@ -1,14 +1,12 @@ import dataclasses import typing -from lite_bootstrap.helpers import is_valid_path from lite_bootstrap.instruments.base import BaseConfig, BaseInstrument @dataclasses.dataclass(kw_only=True, frozen=True) class SwaggerConfig(BaseConfig): - service_static_path: str = "/static" - swagger_path: str = "/docs" + swagger_static_path: str = "/static" swagger_offline_docs: bool = False swagger_extra_params: dict[str, typing.Any] = dataclasses.field(default_factory=dict) @@ -16,7 +14,3 @@ class SwaggerConfig(BaseConfig): @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) class SwaggerInstrument(BaseInstrument): bootstrap_config: SwaggerConfig - not_ready_message = "swagger_path is empty or not valid" - - def is_ready(self) -> bool: - return bool(self.bootstrap_config.swagger_path) and is_valid_path(self.bootstrap_config.swagger_path) diff --git a/lite_bootstrap/fastapi_offline_docs/static/redoc.standalone.js b/lite_bootstrap/static/fastapi_docs/redoc.standalone.js similarity index 100% rename from lite_bootstrap/fastapi_offline_docs/static/redoc.standalone.js rename to lite_bootstrap/static/fastapi_docs/redoc.standalone.js diff --git a/lite_bootstrap/fastapi_offline_docs/static/swagger-ui-bundle.js b/lite_bootstrap/static/fastapi_docs/swagger-ui-bundle.js similarity index 100% rename from lite_bootstrap/fastapi_offline_docs/static/swagger-ui-bundle.js rename to lite_bootstrap/static/fastapi_docs/swagger-ui-bundle.js diff --git a/lite_bootstrap/fastapi_offline_docs/static/swagger-ui.css b/lite_bootstrap/static/fastapi_docs/swagger-ui.css similarity index 100% rename from lite_bootstrap/fastapi_offline_docs/static/swagger-ui.css rename to lite_bootstrap/static/fastapi_docs/swagger-ui.css diff --git a/lite_bootstrap/litestar_swagger_static/asyncapi-web-component.js b/lite_bootstrap/static/litestar_docs/asyncapi-web-component.js similarity index 100% rename from lite_bootstrap/litestar_swagger_static/asyncapi-web-component.js rename to lite_bootstrap/static/litestar_docs/asyncapi-web-component.js diff --git a/lite_bootstrap/litestar_swagger_static/default.min.css b/lite_bootstrap/static/litestar_docs/default.min.css similarity index 100% rename from lite_bootstrap/litestar_swagger_static/default.min.css rename to lite_bootstrap/static/litestar_docs/default.min.css diff --git a/lite_bootstrap/litestar_swagger_static/swagger-ui-bundle.js b/lite_bootstrap/static/litestar_docs/swagger-ui-bundle.js similarity index 100% rename from lite_bootstrap/litestar_swagger_static/swagger-ui-bundle.js rename to lite_bootstrap/static/litestar_docs/swagger-ui-bundle.js diff --git a/lite_bootstrap/litestar_swagger_static/swagger-ui-standalone-preset.js b/lite_bootstrap/static/litestar_docs/swagger-ui-standalone-preset.js similarity index 100% rename from lite_bootstrap/litestar_swagger_static/swagger-ui-standalone-preset.js rename to lite_bootstrap/static/litestar_docs/swagger-ui-standalone-preset.js diff --git a/lite_bootstrap/litestar_swagger_static/swagger-ui.css b/lite_bootstrap/static/litestar_docs/swagger-ui.css similarity index 100% rename from lite_bootstrap/litestar_swagger_static/swagger-ui.css rename to lite_bootstrap/static/litestar_docs/swagger-ui.css diff --git a/tests/test_fastapi_bootstrap.py b/tests/test_fastapi_bootstrap.py index 10f56aa..d1a61f4 100644 --- a/tests/test_fastapi_bootstrap.py +++ b/tests/test_fastapi_bootstrap.py @@ -45,7 +45,7 @@ def test_fastapi_bootstrap(fastapi_config: FastAPIConfig) -> None: assert response.status_code == status.HTTP_200_OK assert response.text - response = test_client.get(fastapi_config.swagger_path) + response = test_client.get(str(application.docs_url)) assert response.status_code == status.HTTP_200_OK assert response.text diff --git a/tests/test_fastapi_offline_docs.py b/tests/test_fastapi_offline_docs.py index 37ba2b9..5724425 100644 --- a/tests/test_fastapi_offline_docs.py +++ b/tests/test_fastapi_offline_docs.py @@ -4,7 +4,7 @@ from fastapi import FastAPI from fastapi.testclient import TestClient -from lite_bootstrap.fastapi_offline_docs.main import enable_offline_docs +from lite_bootstrap.helpers.fastapi_helpers import enable_offline_docs def test_fastapi_offline_docs() -> None: @@ -13,7 +13,7 @@ def test_fastapi_offline_docs() -> None: static_files_handler = "/static2" app = FastAPI(title="Tests", docs_url=docs_url, redoc_url=redoc_url) - enable_offline_docs(app, static_files_handler=static_files_handler) + enable_offline_docs(app, static_path=static_files_handler) with TestClient(app) as client: resp = client.get(docs_url) @@ -31,7 +31,7 @@ def test_fastapi_offline_docs() -> None: def test_fastapi_offline_docs_root_path() -> None: app: FastAPI = FastAPI(title="Tests", root_path="/some-root-path", docs_url="/custom_docs") - enable_offline_docs(app) + enable_offline_docs(app, static_path="/static") with TestClient(app, root_path="/some-root-path") as client: response = client.get("/custom_docs") @@ -47,4 +47,4 @@ def test_fastapi_offline_docs_raises_without_openapi_url() -> None: app = FastAPI(openapi_url=None) with pytest.raises(RuntimeError, match="No app.openapi_url specified"): - enable_offline_docs(app) + enable_offline_docs(app, static_path="/static") diff --git a/tests/test_litestar_bootstrap.py b/tests/test_litestar_bootstrap.py index 5fee231..51db0f1 100644 --- a/tests/test_litestar_bootstrap.py +++ b/tests/test_litestar_bootstrap.py @@ -53,7 +53,7 @@ def test_litestar_bootstrap(litestar_config: LitestarConfig) -> None: response = test_client.get(litestar_config.swagger_path) assert response.status_code == status_codes.HTTP_200_OK - response = test_client.get(f"{litestar_config.service_static_path}/swagger-ui.css") + response = test_client.get(f"{litestar_config.swagger_static_path}/swagger-ui.css") assert response.status_code == status_codes.HTTP_200_OK assert not bootstrapper.is_bootstrapped