Repository: turettn/fastapi_offline (language: python)

Issue:
Configurable doc paths
It's only a slight inconvenience, but could you make it so the docs and redoc urls are configurable, rather than being hardcoded? Perhaps the docs_url and redoc_url could even be used for this, making usage even more similar to FastAPI


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index fc27815..1987b18 100644
--- a/README.md
+++ b/README.md
@@ -46,16 +46,8 @@ async def root():
     return {"message": "Hello World"}
 ```
 
-Any options passed to `FastAPIOffline()` except `docs_url`, `redoc_url`, and `favicon_url` are passed through to `FastAPI()`.
+Any options passed to `FastAPIOffline()` except `docs_url`, `redoc_url`, and `favicon_url` are passed through to `FastAPI()`.  `docs_url` and `redoc_url` are handled by `fastapi-offline`, and use the same syntax as normal `fastapi` library.
 
-# Disabling Docs Page
-
-If you wish to [disable the docs page](https://fastapi.tiangolo.com/tutorial/metadata/#openapi-url), for 
-example in a sub-application, revert to using the default `FastAPI()` class.
-
-```py
-app = FastAPI(openapi_url=None)
-```
 # Using a custom shortcut icon
 
 By default, the FastAPI `favicon.png` is included and used as the shortcut icon on the docs pages.  If you want to use a different one, you can specify it with the `favicon_url` argument:
diff --git a/fastapi_offline/core.py b/fastapi_offline/core.py
index 933f3f8..50a694e 100644
--- a/fastapi_offline/core.py
+++ b/fastapi_offline/core.py
@@ -1,6 +1,6 @@
 """Provide non-CDN-dependent Swagger & Redoc pages to FastAPI"""
 from pathlib import Path
-from typing import Any, TYPE_CHECKING
+from typing import Any, Optional, TYPE_CHECKING
 from fastapi import FastAPI, Request
 from fastapi.openapi.docs import (
     get_redoc_html,
@@ -15,7 +15,12 @@
 _STATIC_PATH = Path(__file__).parent / "static"
 
 
-def FastAPIOffline(*args: Any, **kwargs: Any) -> FastAPI:
+def FastAPIOffline(
+    docs_url: Optional[str] = "/docs",
+    redoc_url: Optional[str] = "/redoc",
+    *args: Any,
+    **kwargs: Any,
+) -> FastAPI:
     """Return a FastAPI obj that doesn't rely on CDN for the documentation page"""
     # Disable the normal doc & redoc pages
     kwargs["docs_url"] = None
@@ -41,45 +46,48 @@ def FastAPIOffline(*args: Any, **kwargs: Any) -> FastAPI:
         name="static-offline-docs",
     )
 
-    # Define the doc and redoc pages, pointing at the right files
-    @app.get("/docs", include_in_schema=False)
-    async def custom_swagger_ui_html(request: Request) -> "Response":
-        root = request.scope.get("root_path")
-
-        if favicon_url is None:
-            favicon = f"{root}/static-offline-docs/favicon.png"
-        else:
-            favicon = favicon_url
-
-        return get_swagger_ui_html(
-            openapi_url=f"{root}{openapi_url}",
-            title=app.title + " - Swagger UI",
-            oauth2_redirect_url=swagger_ui_oauth2_redirect_url,
-            swagger_js_url=f"{root}/static-offline-docs/swagger-ui-bundle.js",
-            swagger_css_url=f"{root}/static-offline-docs/swagger-ui.css",
-            swagger_favicon_url=favicon,
-        )
-
-    @app.get(swagger_ui_oauth2_redirect_url, include_in_schema=False)
-    async def swagger_ui_redirect() -> "Response":
-        return get_swagger_ui_oauth2_redirect_html()
-
-    @app.get("/redoc", include_in_schema=False)
-    async def redoc_html(request: Request) -> "Response":
-        root = request.scope.get("root_path")
-
-        if favicon_url is None:
-            favicon = f"{root}/static-offline-docs/favicon.png"
-        else:
-            favicon = favicon_url
-
-        return get_redoc_html(
-            openapi_url=f"{root}{openapi_url}",
-            title=app.title + " - ReDoc",
-            redoc_js_url=f"{root}/static-offline-docs/redoc.standalone.js",
-            with_google_fonts=False,
-            redoc_favicon_url=favicon,
-        )
+    if docs_url is not None:
+        # Define the doc and redoc pages, pointing at the right files
+        @app.get(docs_url, include_in_schema=False)
+        async def custom_swagger_ui_html(request: Request) -> "Response":
+            root = request.scope.get("root_path")
+
+            if favicon_url is None:
+                favicon = f"{root}/static-offline-docs/favicon.png"
+            else:
+                favicon = favicon_url
+
+            return get_swagger_ui_html(
+                openapi_url=f"{root}{openapi_url}",
+                title=app.title + " - Swagger UI",
+                oauth2_redirect_url=swagger_ui_oauth2_redirect_url,
+                swagger_js_url=f"{root}/static-offline-docs/swagger-ui-bundle.js",
+                swagger_css_url=f"{root}/static-offline-docs/swagger-ui.css",
+                swagger_favicon_url=favicon,
+            )
+
+        @app.get(swagger_ui_oauth2_redirect_url, include_in_schema=False)
+        async def swagger_ui_redirect() -> "Response":
+            return get_swagger_ui_oauth2_redirect_html()
+
+    if redoc_url is not None:
+
+        @app.get(redoc_url, include_in_schema=False)
+        async def redoc_html(request: Request) -> "Response":
+            root = request.scope.get("root_path")
+
+            if favicon_url is None:
+                favicon = f"{root}/static-offline-docs/favicon.png"
+            else:
+                favicon = favicon_url
+
+            return get_redoc_html(
+                openapi_url=f"{root}{openapi_url}",
+                title=app.title + " - ReDoc",
+                redoc_js_url=f"{root}/static-offline-docs/redoc.standalone.js",
+                with_google_fonts=False,
+                redoc_favicon_url=favicon,
+            )
 
     # Return the FastAPI object
     return app


Test patch:
diff --git a/tests/test_altpaths.py b/tests/test_altpaths.py
new file mode 100644
index 0000000..6e23895
--- /dev/null
+++ b/tests/test_altpaths.py
@@ -0,0 +1,42 @@
+from fastapi.testclient import TestClient
+from fastapi import FastAPI
+from fastapi_offline import FastAPIOffline
+
+DOC = "/asdf"
+REDOC = "/qwerty"
+
+# Custom paths for both
+app1 = FastAPIOffline(docs_url=DOC, redoc_url=REDOC)
+client1 = TestClient(app1)
+
+# Disable redoc
+app2 = FastAPIOffline(docs_url=DOC, redoc_url=None)
+client2 = TestClient(app2)
+
+# Disable Swagger
+app3 = FastAPIOffline(docs_url=None, redoc_url=REDOC)
+client3 = TestClient(app3)
+
+
+def test_swagger():
+    """Make sure Swagger appears at the right place"""
+    assert client1.get("/docs").status_code == 404
+    assert client1.get(DOC).status_code == 200
+
+    assert client2.get("/docs").status_code == 404
+    assert client2.get(DOC).status_code == 200
+
+    assert client3.get("/docs").status_code == 404
+    assert client3.get(DOC).status_code == 404
+
+
+def test_custom_redocs():
+    """Make sure Redoc appears at the right place"""
+    assert client1.get("/redoc").status_code == 404
+    assert client1.get(REDOC).status_code == 200
+
+    assert client2.get("/redoc").status_code == 404
+    assert client2.get(REDOC).status_code == 404
+
+    assert client3.get("/redoc").status_code == 404
+    assert client3.get(REDOC).status_code == 200