diff --git a/blacksheep/server/openapi/common.py b/blacksheep/server/openapi/common.py index 08c2fe75..9909e5d6 100644 --- a/blacksheep/server/openapi/common.py +++ b/blacksheep/server/openapi/common.py @@ -34,7 +34,7 @@ from blacksheep.server.files.static import get_response_for_static_content from blacksheep.server.routing import Route, Router -from .ui import SwaggerUIProvider, UIOptions, UIProvider +from .ui import CdnOptions, SwaggerUIProvider, UIOptions, UIProvider T = TypeVar("T") @@ -167,6 +167,7 @@ def __init__( yaml_spec_path: str = "/openapi.yaml", preferred_format: Format = Format.JSON, anonymous_access: bool = True, + swagger_ui_cdn: Optional[CdnOptions] = None, ) -> None: self._handlers_docs: Dict[Any, EndpointDocs] = {} self.use_docstrings: bool = True @@ -177,7 +178,9 @@ def __init__( self._yaml_docs: bytes = b"" self.preferred_format = preferred_format self.anonymous_access = anonymous_access - self.ui_providers: List[UIProvider] = [SwaggerUIProvider(ui_path)] + self.ui_providers: List[UIProvider] = [ + SwaggerUIProvider(ui_path, swagger_ui_cdn) + ] self._types_schemas = {} self.events = OpenAPIEvents(self) self.handle_optional_response_with_404 = True diff --git a/blacksheep/server/openapi/ui.py b/blacksheep/server/openapi/ui.py index fe24c6ff..41cd285b 100644 --- a/blacksheep/server/openapi/ui.py +++ b/blacksheep/server/openapi/ui.py @@ -37,8 +37,6 @@ class UIProvider(ABC): cdn: CdnOptions ui_path: str - _default_cdn: CdnOptions - def __init__( self, ui_path: str, @@ -46,7 +44,7 @@ def __init__( ) -> None: super().__init__() self.ui_path = ui_path - self.cdn = cdn if cdn else self._default_cdn + self.cdn = cdn if cdn else self.default_cdn @abstractmethod def build_ui(self, options: UIOptions) -> None: @@ -60,10 +58,12 @@ def get_ui_handler(self) -> Callable[[Request], Response]: Returns a request handler for the route that serves a UI. """ + @property + def default_cdn(self) -> CdnOptions: + ... -class SwaggerUIProvider(UIProvider): - _default_cdn = CdnOptions(SWAGGER_UI_CDN, SWAGGER_UI_CSS, SWAGGER_UI_FONT) +class SwaggerUIProvider(UIProvider): def __init__( self, ui_path: str = "/docs", @@ -98,10 +98,12 @@ def get_open_api_ui(request: Request) -> Response: return get_open_api_ui + @property + def default_cdn(self) -> CdnOptions: + return CdnOptions(SWAGGER_UI_CDN, SWAGGER_UI_CSS, SWAGGER_UI_FONT) -class ReDocUIProvider(UIProvider): - _default_cdn = CdnOptions(REDOC_UI_CDN, REDOC_UI_CSS, REDOC_UI_FONT) +class ReDocUIProvider(UIProvider): def __init__( self, ui_path: str = "/redocs", cdn: Optional[CdnOptions] = None ) -> None: @@ -133,3 +135,7 @@ def get_open_api_ui(request: Request) -> Response: ) return get_open_api_ui + + @property + def default_cdn(self) -> CdnOptions: + return CdnOptions(REDOC_UI_CDN, REDOC_UI_CSS, REDOC_UI_FONT) diff --git a/blacksheep/server/openapi/v3.py b/blacksheep/server/openapi/v3.py index 3ee247ce..44224f77 100644 --- a/blacksheep/server/openapi/v3.py +++ b/blacksheep/server/openapi/v3.py @@ -69,6 +69,7 @@ ResponseStatusType, response_status_to_str, ) +from .ui import CdnOptions try: from pydantic import BaseModel # type: ignore @@ -376,6 +377,7 @@ def __init__( SecuritySchemes, ] ] = None, + swagger_ui_cdn: Optional[CdnOptions] = None, ) -> None: super().__init__( ui_path=ui_path, @@ -383,6 +385,7 @@ def __init__( yaml_spec_path=yaml_spec_path, preferred_format=preferred_format, anonymous_access=anonymous_access, + swagger_ui_cdn=swagger_ui_cdn, ) self.info = info self._tags = tags diff --git a/itests/app_4.py b/itests/app_4.py index b86e525a..4e9e3258 100644 --- a/itests/app_4.py +++ b/itests/app_4.py @@ -5,15 +5,20 @@ from datetime import datetime import uvicorn +from openapidocs.v3 import Info from blacksheep import JSONContent, Response from blacksheep.server import Application from blacksheep.server.bindings import FromJSON from blacksheep.server.compression import use_gzip_compression +from blacksheep.server.openapi.ui import CdnOptions, ReDocUIProvider +from blacksheep.server.openapi.v3 import OpenAPIHandler from blacksheep.server.responses import json from blacksheep.server.websocket import WebSocket from blacksheep.settings.json import default_json_dumps, json_settings +from .utils import foo_cdn + SINGLE_PID = None @@ -150,6 +155,21 @@ async def echo_json(websocket: WebSocket): await websocket.send_json(msg) +docs = OpenAPIHandler( + info=Info(title="Cats API", version="0.0.1"), + swagger_ui_cdn=CdnOptions( + js_cdn_url=foo_cdn("swag-js"), css_cdn_url=foo_cdn("swag-css") + ), +) +docs.ui_providers.append( + ReDocUIProvider( + cdn=CdnOptions( + js_cdn_url=foo_cdn("redoc-js"), fontset_cdn_url=foo_cdn("redoc-fonts") + ) + ) +) +docs.bind_app(app_4) + if __name__ == "__main__": configure_json_settings() uvicorn.run(app_4, host="127.0.0.1", port=44557, log_level="debug") diff --git a/itests/test_server.py b/itests/test_server.py index 905d94ad..8d83efe7 100644 --- a/itests/test_server.py +++ b/itests/test_server.py @@ -12,7 +12,7 @@ from .client_fixtures import get_static_path from .server_fixtures import * # NoQA -from .utils import assert_files_equals, ensure_success +from .utils import assert_files_equals, ensure_success, foo_cdn def test_hello_world(session_1): @@ -421,6 +421,77 @@ def test_open_api_redoc_ui(session_2): ) +def test_open_api_ui_custom_cdn(session_4): + response = session_4.get("/docs") + + assert response.status_code == 200 + text = response.text + assert ( + text.strip() + == f""" + + +
+