]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
✨ Enable configuring Swagger UI parameters (#2568)
authorJohn Riebold <jmriebold@gmail.com>
Sun, 16 Jan 2022 19:26:24 +0000 (11:26 -0800)
committerGitHub <noreply@github.com>
Sun, 16 Jan 2022 19:26:24 +0000 (20:26 +0100)
Co-authored-by: Artem Ivanov <artem@worklife.io>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
12 files changed:
docs/en/docs/advanced/extending-openapi.md
docs/en/docs/img/tutorial/extending-openapi/image02.png [new file with mode: 0644]
docs/en/docs/img/tutorial/extending-openapi/image03.png [new file with mode: 0644]
docs/en/docs/img/tutorial/extending-openapi/image04.png [new file with mode: 0644]
docs_src/extending_openapi/tutorial003.py [new file with mode: 0644]
docs_src/extending_openapi/tutorial004.py [new file with mode: 0644]
docs_src/extending_openapi/tutorial005.py [new file with mode: 0644]
fastapi/applications.py
fastapi/openapi/docs.py
tests/test_tutorial/test_extending_openapi/test_tutorial003.py [new file with mode: 0644]
tests/test_tutorial/test_extending_openapi/test_tutorial004.py [new file with mode: 0644]
tests/test_tutorial/test_extending_openapi/test_tutorial005.py [new file with mode: 0644]

index d8d280ba67276b898bde7e50a67eaf5579043020..d1b14bc003fde6f329f27175d341766c212481c8 100644 (file)
@@ -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 <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>, 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 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">Swagger UI parameters</a>.
+
+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:
+
+<img src="/img/tutorial/extending-openapi/image02.png">
+
+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:
+
+<img src="/img/tutorial/extending-openapi/image03.png">
+
+### 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:
+
+<img src="/img/tutorial/extending-openapi/image04.png">
+
+### 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 <a href="https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration" class="external-link" target="_blank">docs for Swagger UI parameters</a>.
+
+### 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 (file)
index 0000000..91453fb
Binary files /dev/null and b/docs/en/docs/img/tutorial/extending-openapi/image02.png differ
diff --git a/docs/en/docs/img/tutorial/extending-openapi/image03.png b/docs/en/docs/img/tutorial/extending-openapi/image03.png
new file mode 100644 (file)
index 0000000..e45a77d
Binary files /dev/null and b/docs/en/docs/img/tutorial/extending-openapi/image03.png differ
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 (file)
index 0000000..394d2bb
Binary files /dev/null and b/docs/en/docs/img/tutorial/extending-openapi/image04.png differ
diff --git a/docs_src/extending_openapi/tutorial003.py b/docs_src/extending_openapi/tutorial003.py
new file mode 100644 (file)
index 0000000..6c24ce7
--- /dev/null
@@ -0,0 +1,8 @@
+from fastapi import FastAPI
+
+app = FastAPI(swagger_ui_parameters={"syntaxHighlight": False})
+
+
+@app.get("/users/{username}")
+async def read_user(username: str):
+    return {"message": f"Hello {username}"}
diff --git a/docs_src/extending_openapi/tutorial004.py b/docs_src/extending_openapi/tutorial004.py
new file mode 100644 (file)
index 0000000..cc569ce
--- /dev/null
@@ -0,0 +1,8 @@
+from fastapi import FastAPI
+
+app = FastAPI(swagger_ui_parameters={"syntaxHighlight.theme": "obsidian"})
+
+
+@app.get("/users/{username}")
+async def read_user(username: str):
+    return {"message": f"Hello {username}"}
diff --git a/docs_src/extending_openapi/tutorial005.py b/docs_src/extending_openapi/tutorial005.py
new file mode 100644 (file)
index 0000000..b4449f5
--- /dev/null
@@ -0,0 +1,8 @@
+from fastapi import FastAPI
+
+app = FastAPI(swagger_ui_parameters={"deepLinking": False})
+
+
+@app.get("/users/{username}")
+async def read_user(username: str):
+    return {"message": f"Hello {username}"}
index 0c25026e2bb1e97523314321e8d05522eac46b62..d71d4190abc10571cc085ba0e704ff6230e2e788 100644 (file)
@@ -65,6 +65,7 @@ class FastAPI(Starlette):
         callbacks: Optional[List[BaseRoute]] = None,
         deprecated: Optional[bool] = None,
         include_in_schema: bool = True,
+        swagger_ui_parameters: Optional[Dict[str, Any]] = None,
         **extra: Any,
     ) -> None:
         self._debug: bool = debug
@@ -120,6 +121,7 @@ class FastAPI(Starlette):
         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 @@ class FastAPI(Starlette):
                     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)
index fd22e4e8c167ddc6aa65d46942ff41dc6a2a3af8..1be90d188fadef68438e7845c6b2fcdfa826891e 100644 (file)
@@ -4,6 +4,14 @@ from typing import Any, Dict, Optional
 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"""
     <!DOCTYPE html>
@@ -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 (file)
index 0000000..0184dd9
--- /dev/null
@@ -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 (file)
index 0000000..4f76151
--- /dev/null
@@ -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 (file)
index 0000000..24aeb93
--- /dev/null
@@ -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"}