Skip to content

Commit f54d8d5

Browse files
euri10tiangolo
authored andcommitted
✨ Make Swagger UI and ReDoc parameterizable to host offline assets for docs (fastapi#112)
1 parent 56ab106 commit f54d8d5

File tree

2 files changed

+97
-44
lines changed

2 files changed

+97
-44
lines changed

fastapi/openapi/docs.py

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,77 @@
11
from starlette.responses import HTMLResponse
22

33

4-
def get_swagger_ui_html(*, openapi_url: str, title: str) -> HTMLResponse:
5-
return HTMLResponse(
6-
"""
4+
def get_swagger_ui_html(
5+
*,
6+
openapi_url: str,
7+
title: str,
8+
swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui-bundle.js",
9+
swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui.css",
10+
swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
11+
) -> HTMLResponse:
12+
html = f"""
713
<! doctype html>
814
<html>
915
<head>
10-
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui.css">
11-
<link rel="shortcut icon" href="https://fastapi.tiangolo.com/img/favicon.png">
12-
<title>
13-
"""
14-
+ title
15-
+ """
16-
</title>
16+
<link type="text/css" rel="stylesheet" href="{swagger_css_url}">
17+
<link rel="shortcut icon" href="{swagger_favicon_url}">
18+
<title>{title}</title>
1719
</head>
1820
<body>
1921
<div id="swagger-ui">
2022
</div>
21-
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
23+
<script src="{swagger_js_url}"></script>
2224
<!-- `SwaggerUIBundle` is now available on the page -->
2325
<script>
24-
25-
const ui = SwaggerUIBundle({
26-
url: '"""
27-
+ openapi_url
28-
+ """',
26+
const ui = SwaggerUIBundle({{
27+
url: '{openapi_url}',
2928
dom_id: '#swagger-ui',
3029
presets: [
3130
SwaggerUIBundle.presets.apis,
3231
SwaggerUIBundle.SwaggerUIStandalonePreset
3332
],
34-
layout: "BaseLayout",
35-
deepLinking: true
36-
37-
})
33+
layout: "BaseLayout"
34+
35+
}})
3836
</script>
3937
</body>
4038
</html>
4139
"""
42-
)
40+
return HTMLResponse(html)
41+
4342

43+
def get_redoc_html(
44+
*,
45+
openapi_url: str,
46+
title: str,
47+
redoc_js_url: str = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
48+
redoc_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
49+
) -> HTMLResponse:
4450

45-
def get_redoc_html(*, openapi_url: str, title: str) -> HTMLResponse:
46-
return HTMLResponse(
47-
"""
51+
html = f"""
4852
<!DOCTYPE html>
49-
<html>
50-
<head>
51-
<title>
52-
"""
53-
+ title
54-
+ """
55-
</title>
53+
<html>
54+
<head>
55+
<title>{title}</title>
5656
<!-- needed for adaptive design -->
5757
<meta charset="utf-8"/>
5858
<meta name="viewport" content="width=device-width, initial-scale=1">
5959
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
60-
<link rel="shortcut icon" href="https://fastapi.tiangolo.com/img/favicon.png">
61-
60+
<link rel="shortcut icon" href="{redoc_favicon_url}">
6261
<!--
6362
ReDoc doesn't change outer page styles
6463
-->
6564
<style>
66-
body {
65+
body {{
6766
margin: 0;
6867
padding: 0;
69-
}
68+
}}
7069
</style>
71-
</head>
72-
<body>
73-
<redoc spec-url='"""
74-
+ openapi_url
75-
+ """'></redoc>
76-
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
77-
</body>
78-
</html>
70+
</head>
71+
<body>
72+
<redoc spec-url="{openapi_url}"></redoc>
73+
<script src="{redoc_js_url}"> </script>
74+
</body>
75+
</html>
7976
"""
80-
)
77+
return HTMLResponse(html)

tests/test_local_docs.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import inspect
2+
3+
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
4+
5+
6+
def test_strings_in_generated_swagger():
7+
sig = inspect.signature(get_swagger_ui_html)
8+
swagger_js_url = sig.parameters.get("swagger_js_url").default
9+
swagger_css_url = sig.parameters.get("swagger_css_url").default
10+
swagger_favicon_url = sig.parameters.get("swagger_favicon_url").default
11+
html = get_swagger_ui_html(openapi_url="/docs", title="title")
12+
body_content = html.body.decode()
13+
assert swagger_js_url in body_content
14+
assert swagger_css_url in body_content
15+
assert swagger_favicon_url in body_content
16+
17+
18+
def test_strings_in_custom_swagger():
19+
swagger_js_url = "swagger_fake_file.js"
20+
swagger_css_url = "swagger_fake_file.css"
21+
swagger_favicon_url = "swagger_fake_file.png"
22+
html = get_swagger_ui_html(
23+
openapi_url="/docs",
24+
title="title",
25+
swagger_js_url=swagger_js_url,
26+
swagger_css_url=swagger_css_url,
27+
swagger_favicon_url=swagger_favicon_url,
28+
)
29+
body_content = html.body.decode()
30+
assert swagger_js_url in body_content
31+
assert swagger_css_url in body_content
32+
assert swagger_favicon_url in body_content
33+
34+
35+
def test_strings_in_generated_redoc():
36+
sig = inspect.signature(get_redoc_html)
37+
redoc_js_url = sig.parameters.get("redoc_js_url").default
38+
redoc_favicon_url = sig.parameters.get("redoc_favicon_url").default
39+
html = get_redoc_html(openapi_url="/docs", title="title")
40+
body_content = html.body.decode()
41+
assert redoc_js_url in body_content
42+
assert redoc_favicon_url in body_content
43+
44+
45+
def test_strings_in_custom_redoc():
46+
redoc_js_url = "fake_redoc_file.js"
47+
redoc_favicon_url = "fake_redoc_file.png"
48+
html = get_redoc_html(
49+
openapi_url="/docs",
50+
title="title",
51+
redoc_js_url=redoc_js_url,
52+
redoc_favicon_url=redoc_favicon_url,
53+
)
54+
body_content = html.body.decode()
55+
assert redoc_js_url in body_content
56+
assert redoc_favicon_url in body_content

0 commit comments

Comments
 (0)