Repository: greyli/apiflask
Pull request #64 (2021-06-13 03:39:44)

Support set the url prefix of the OpenAPI blueprint
Example usage:

```py
app = APIFlask(__name__, openapi_blueprint_url_prefix='/api')
```

Then the spec and docs will be available at `/api/openapi.json`, `/api/docs`, etc.

Checklist:

- [x] Add tests that demonstrate the correct behavior of the change. Tests should fail without the change.
- [ ] Add or update relevant docs, in the `docs` folder and in code docstring.
- [x] Add an entry in `CHANGES.md` summarizing the change and linking to the issue and your username.
- [x] Add `*Version Changed*` or `*Version Added*` note in any relevant docs and docstring.
- [x] Run `pytest` and `tox`, no tests failed.



Patch:
diff --git a/CHANGES.md b/CHANGES.md
index 1aace94..bbc0b76 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -5,9 +5,12 @@
 - Support using async error processor and async spec processor
 ([pull #57][pull_57]).
 - Fix auto-tag support for nesting blueprint ([pull #58][pull_58]).
+- Support set the url prefix of the OpenAPI blueprint with the 
+`openapi_blueprint_url_prefix` argument ([pull #64][pull_64]).
 
 [pull_57]: https://github.com/greyli/apiflask/pull/57
 [pull_58]: https://github.com/greyli/apiflask/pull/58
+[pull_64]: https://github.com/greyli/apiflask/pull/64
 
 
 ## Version 0.6.3
diff --git a/apiflask/app.py b/apiflask/app.py
index 1c583e4..7e4cfeb 100644
--- a/apiflask/app.py
+++ b/apiflask/app.py
@@ -199,6 +199,7 @@ class APIFlask(Flask):
         docs_path: str = '/docs',
         docs_oauth2_redirect_path: str = '/docs/oauth2-redirect',
         redoc_path: str = '/redoc',
+        openapi_blueprint_url_prefix: t.Optional[str] = None,
         json_errors: bool = True,
         enable_openapi: bool = True,
         static_url_path: t.Optional[str] = None,
@@ -226,10 +227,17 @@ class APIFlask(Flask):
             docs_path: The path to Swagger UI documentation, defaults to `/docs`.
             docs_oauth2_redirect_path: The path to Swagger UI OAuth redirect.
             redoc_path: The path to Redoc documentation, defaults to `/redoc`.
+            openapi_blueprint_url_prefix: The url prefix of the OpenAPI blueprint. This
+                prefix will append before all the OpenAPI-related paths (`sepc_path`,
+                `docs_path`, etc.), defaults to `None`.
             json_errors: If `True`, APIFlask will return a JSON response for HTTP errors.
             enable_openapi: If `False`, will disable OpenAPI spec and API docs views.
 
         Other keyword arguments are directly passed to `flask.Flask`.
+
+        *Version Changed: 0.7.0*
+
+        - Add `openapi_blueprint_url_prefix` argument.
         """
         super(APIFlask, self).__init__(
             import_name,
@@ -253,6 +261,7 @@ class APIFlask(Flask):
         self.docs_path = docs_path
         self.redoc_path = redoc_path
         self.docs_oauth2_redirect_path = docs_oauth2_redirect_path
+        self.openapi_blueprint_url_prefix = openapi_blueprint_url_prefix
         self.enable_openapi = enable_openapi
         self.json_errors = json_errors
 
@@ -427,7 +436,8 @@ class APIFlask(Flask):
             __name__,
             template_folder='templates',
             static_folder='static',
-            static_url_path='/apiflask'
+            static_url_path='/apiflask',
+            url_prefix=self.openapi_blueprint_url_prefix
         )
 
         if self.spec_path:


Test patch:
diff --git a/tests/test_openapi_blueprint.py b/tests/test_openapi_blueprint.py
index fad423e..146bc2e 100644
--- a/tests/test_openapi_blueprint.py
+++ b/tests/test_openapi_blueprint.py
@@ -121,3 +121,17 @@ def test_redoc(client):
     rv = client.get('/redoc')
     assert rv.status_code == 200
     assert b'redoc.standalone.js' in rv.data
+
+
+def test_openapi_blueprint_url_prefix(app):
+    assert app.openapi_blueprint_url_prefix is None
+
+    prefix = '/api'
+    app = APIFlask(__name__, openapi_blueprint_url_prefix=prefix)
+    assert app.openapi_blueprint_url_prefix == prefix
+
+    client = app.test_client()
+    rv = client.get('/docs')
+    assert rv.status_code == 404
+    rv = client.get(f'{prefix}/docs')
+    assert rv.status_code == 200