From a85aa125d24acb225b7b722bb4f3e331a21ec267 Mon Sep 17 00:00:00 2001 From: John Riebold Date: Sun, 16 Jan 2022 11:26:24 -0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Enable=20configuring=20Swagger=20UI?= =?UTF-8?q?=20parameters=20(#2568)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Artem Ivanov Co-authored-by: Sebastián Ramírez --- docs/en/docs/advanced/extending-openapi.md | 79 ++++++++++++++++++ .../tutorial/extending-openapi/image02.png | Bin 0 -> 11292 bytes .../tutorial/extending-openapi/image03.png | Bin 0 -> 11176 bytes .../tutorial/extending-openapi/image04.png | Bin 0 -> 11180 bytes docs_src/extending_openapi/tutorial003.py | 8 ++ docs_src/extending_openapi/tutorial004.py | 8 ++ docs_src/extending_openapi/tutorial005.py | 8 ++ fastapi/applications.py | 3 + fastapi/openapi/docs.py | 22 +++-- .../test_tutorial003.py | 41 +++++++++ .../test_tutorial004.py | 44 ++++++++++ .../test_tutorial005.py | 44 ++++++++++ 12 files changed, 251 insertions(+), 6 deletions(-) create mode 100644 docs/en/docs/img/tutorial/extending-openapi/image02.png create mode 100644 docs/en/docs/img/tutorial/extending-openapi/image03.png create mode 100644 docs/en/docs/img/tutorial/extending-openapi/image04.png create mode 100644 docs_src/extending_openapi/tutorial003.py create mode 100644 docs_src/extending_openapi/tutorial004.py create mode 100644 docs_src/extending_openapi/tutorial005.py create mode 100644 tests/test_tutorial/test_extending_openapi/test_tutorial003.py create mode 100644 tests/test_tutorial/test_extending_openapi/test_tutorial004.py create mode 100644 tests/test_tutorial/test_extending_openapi/test_tutorial005.py diff --git a/docs/en/docs/advanced/extending-openapi.md b/docs/en/docs/advanced/extending-openapi.md index d8d280ba67276..d1b14bc003fde 100644 --- a/docs/en/docs/advanced/extending-openapi.md +++ b/docs/en/docs/advanced/extending-openapi.md @@ -233,3 +233,82 @@ Now, to be able to test that everything works, create a *path operation*: Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page. And even without Internet, you would be able to see the docs for your API and interact with it. + +## Configuring Swagger UI + +You can configure some extra Swagger UI parameters. + +To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. + +`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. + +FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. + +### Disable Syntax Highlighting + +For example, you could disable syntax highlighting in Swagger UI. + +Without changing the settings, syntax highlighting is enabled by default: + + + +But you can disable it by setting `syntaxHighlight` to `False`: + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial003.py!} +``` + +...and then Swagger UI won't show the syntax highlighting anymore: + + + +### Change the Theme + +The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial004.py!} +``` + +That configuration would change the syntax highlighting color theme: + + + +### Change Default Swagger UI Parameters + +FastAPI includes some default configuration parameters appropriate for most of the use cases. + +It includes these default configurations: + +```Python +{!../../../fastapi/openapi/docs.py[ln:7-13]!} +``` + +You can override any of them by setting a different value in the argument `swagger_ui_parameters`. + +For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial005.py!} +``` + +### Other Swagger UI Parameters + +To see all the other possible configurations you can use, read the official docs for Swagger UI parameters. + +### JavaScript-only settings + +Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). + +FastAPI also includes these JavaScript-only `presets` settings: + +```JavaScript +presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIBundle.SwaggerUIStandalonePreset +] +``` + +These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. + +If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. diff --git a/docs/en/docs/img/tutorial/extending-openapi/image02.png b/docs/en/docs/img/tutorial/extending-openapi/image02.png new file mode 100644 index 0000000000000000000000000000000000000000..91453fb56f687b99fc725afa0a7fa07eb1955137 GIT binary patch literal 11292 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 literal 0 HcmV?d00001 diff --git a/docs/en/docs/img/tutorial/extending-openapi/image04.png b/docs/en/docs/img/tutorial/extending-openapi/image04.png new file mode 100644 index 0000000000000000000000000000000000000000..394d2bb431ab5eadaa919087ce1618f996bd829e GIT binary patch literal 11180 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 None: self._debug: bool = debug @@ -120,6 +121,7 @@ def __init__( self.redoc_url = redoc_url self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url self.swagger_ui_init_oauth = swagger_ui_init_oauth + self.swagger_ui_parameters = swagger_ui_parameters self.extra = extra self.dependency_overrides: Dict[Callable[..., Any], Callable[..., Any]] = {} @@ -174,6 +176,7 @@ async def swagger_ui_html(req: Request) -> HTMLResponse: title=self.title + " - Swagger UI", oauth2_redirect_url=oauth2_redirect_url, init_oauth=self.swagger_ui_init_oauth, + swagger_ui_parameters=self.swagger_ui_parameters, ) self.add_route(self.docs_url, swagger_ui_html, include_in_schema=False) diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py index fd22e4e8c167d..1be90d188fade 100644 --- a/fastapi/openapi/docs.py +++ b/fastapi/openapi/docs.py @@ -4,6 +4,14 @@ from fastapi.encoders import jsonable_encoder from starlette.responses import HTMLResponse +swagger_ui_default_parameters = { + "dom_id": "#swagger-ui", + "layout": "BaseLayout", + "deepLinking": True, + "showExtensions": True, + "showCommonExtensions": True, +} + def get_swagger_ui_html( *, @@ -14,7 +22,11 @@ def get_swagger_ui_html( swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png", oauth2_redirect_url: Optional[str] = None, init_oauth: Optional[Dict[str, Any]] = None, + swagger_ui_parameters: Optional[Dict[str, Any]] = None, ) -> HTMLResponse: + current_swagger_ui_parameters = swagger_ui_default_parameters.copy() + if swagger_ui_parameters: + current_swagger_ui_parameters.update(swagger_ui_parameters) html = f""" @@ -34,19 +46,17 @@ def get_swagger_ui_html( url: '{openapi_url}', """ + for key, value in current_swagger_ui_parameters.items(): + html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n" + if oauth2_redirect_url: html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," html += """ - dom_id: '#swagger-ui', - presets: [ + presets: [ SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset ], - layout: "BaseLayout", - deepLinking: true, - showExtensions: true, - showCommonExtensions: true })""" if init_oauth: diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial003.py b/tests/test_tutorial/test_extending_openapi/test_tutorial003.py new file mode 100644 index 0000000000000..0184dd9f8384b --- /dev/null +++ b/tests/test_tutorial/test_extending_openapi/test_tutorial003.py @@ -0,0 +1,41 @@ +from fastapi.testclient import TestClient + +from docs_src.extending_openapi.tutorial003 import app + +client = TestClient(app) + + +def test_swagger_ui(): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert ( + '"syntaxHighlight": false' in response.text + ), "syntaxHighlight should be included and converted to JSON" + assert ( + '"dom_id": "#swagger-ui"' in response.text + ), "default configs should be preserved" + assert "presets: [" in response.text, "default configs should be preserved" + assert ( + "SwaggerUIBundle.presets.apis," in response.text + ), "default configs should be preserved" + assert ( + "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text + ), "default configs should be preserved" + assert ( + '"layout": "BaseLayout",' in response.text + ), "default configs should be preserved" + assert ( + '"deepLinking": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showExtensions": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showCommonExtensions": true,' in response.text + ), "default configs should be preserved" + + +def test_get_users(): + response = client.get("/users/foo") + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello foo"} diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial004.py b/tests/test_tutorial/test_extending_openapi/test_tutorial004.py new file mode 100644 index 0000000000000..4f7615126a0cd --- /dev/null +++ b/tests/test_tutorial/test_extending_openapi/test_tutorial004.py @@ -0,0 +1,44 @@ +from fastapi.testclient import TestClient + +from docs_src.extending_openapi.tutorial004 import app + +client = TestClient(app) + + +def test_swagger_ui(): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert ( + '"syntaxHighlight": false' not in response.text + ), "not used parameters should not be included" + assert ( + '"syntaxHighlight.theme": "obsidian"' in response.text + ), "parameters with middle dots should be included in a JSON compatible way" + assert ( + '"dom_id": "#swagger-ui"' in response.text + ), "default configs should be preserved" + assert "presets: [" in response.text, "default configs should be preserved" + assert ( + "SwaggerUIBundle.presets.apis," in response.text + ), "default configs should be preserved" + assert ( + "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text + ), "default configs should be preserved" + assert ( + '"layout": "BaseLayout",' in response.text + ), "default configs should be preserved" + assert ( + '"deepLinking": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showExtensions": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showCommonExtensions": true,' in response.text + ), "default configs should be preserved" + + +def test_get_users(): + response = client.get("/users/foo") + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello foo"} diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial005.py b/tests/test_tutorial/test_extending_openapi/test_tutorial005.py new file mode 100644 index 0000000000000..24aeb93db32ec --- /dev/null +++ b/tests/test_tutorial/test_extending_openapi/test_tutorial005.py @@ -0,0 +1,44 @@ +from fastapi.testclient import TestClient + +from docs_src.extending_openapi.tutorial005 import app + +client = TestClient(app) + + +def test_swagger_ui(): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert ( + '"deepLinking": false,' in response.text + ), "overridden configs should be preserved" + assert ( + '"deepLinking": true' not in response.text + ), "overridden configs should not include the old value" + assert ( + '"syntaxHighlight": false' not in response.text + ), "not used parameters should not be included" + assert ( + '"dom_id": "#swagger-ui"' in response.text + ), "default configs should be preserved" + assert "presets: [" in response.text, "default configs should be preserved" + assert ( + "SwaggerUIBundle.presets.apis," in response.text + ), "default configs should be preserved" + assert ( + "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text + ), "default configs should be preserved" + assert ( + '"layout": "BaseLayout",' in response.text + ), "default configs should be preserved" + assert ( + '"showExtensions": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showCommonExtensions": true,' in response.text + ), "default configs should be preserved" + + +def test_get_users(): + response = client.get("/users/foo") + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello foo"}