Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions lite_bootstrap/bootstrappers/fastapi_bootstrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
)


Expand Down
16 changes: 10 additions & 6 deletions lite_bootstrap/bootstrappers/litestar_bootstrapper.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"
),
),
)
Expand All @@ -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"]
)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import os
import pathlib
import typing

from lite_bootstrap import import_checker
Expand All @@ -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"
Expand All @@ -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",
)
File renamed without changes.
9 changes: 3 additions & 6 deletions lite_bootstrap/instruments/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion lite_bootstrap/instruments/prometheus_instrument.py
Original file line number Diff line number Diff line change
@@ -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


Expand Down
8 changes: 1 addition & 7 deletions lite_bootstrap/instruments/swagger_instrument.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
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)


@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)
2 changes: 1 addition & 1 deletion tests/test_fastapi_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions tests/test_fastapi_offline_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
Expand All @@ -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")
Expand All @@ -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")
2 changes: 1 addition & 1 deletion tests/test_litestar_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading