Coverage for fastapi/openapi/docs.py: 100%
30 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1import json 1$%'()*+
2from typing import Any, Dict, Optional 1$%'()*+
4from annotated_doc import Doc 1$%'()*+
5from fastapi.encoders import jsonable_encoder 1$%'()*+
6from starlette.responses import HTMLResponse 1$%'()*+
7from typing_extensions import Annotated 1$%'()*+
9swagger_ui_default_parameters: Annotated[ 1$%'()*+
10 Dict[str, Any],
11 Doc(
12 """
13 Default configurations for Swagger UI.
15 You can use it as a template to add any other configurations needed.
16 """
17 ),
18] = {
19 "dom_id": "#swagger-ui",
20 "layout": "BaseLayout",
21 "deepLinking": True,
22 "showExtensions": True,
23 "showCommonExtensions": True,
24}
27def get_swagger_ui_html( 1$%'()*+J
28 *,
29 openapi_url: Annotated[
30 str,
31 Doc(
32 """
33 The OpenAPI URL that Swagger UI should load and use.
35 This is normally done automatically by FastAPI using the default URL
36 `/openapi.json`.
37 """
38 ),
39 ],
40 title: Annotated[
41 str,
42 Doc(
43 """
44 The HTML `<title>` content, normally shown in the browser tab.
45 """
46 ),
47 ],
48 swagger_js_url: Annotated[
49 str,
50 Doc(
51 """
52 The URL to use to load the Swagger UI JavaScript.
54 It is normally set to a CDN URL.
55 """
56 ),
57 ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
58 swagger_css_url: Annotated[
59 str,
60 Doc(
61 """
62 The URL to use to load the Swagger UI CSS.
64 It is normally set to a CDN URL.
65 """
66 ),
67 ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
68 swagger_favicon_url: Annotated[
69 str,
70 Doc(
71 """
72 The URL of the favicon to use. It is normally shown in the browser tab.
73 """
74 ),
75 ] = "https://fastapi.tiangolo.com/img/favicon.png",
76 oauth2_redirect_url: Annotated[
77 Optional[str],
78 Doc(
79 """
80 The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
81 """
82 ),
83 ] = None,
84 init_oauth: Annotated[
85 Optional[Dict[str, Any]],
86 Doc(
87 """
88 A dictionary with Swagger UI OAuth2 initialization configurations.
89 """
90 ),
91 ] = None,
92 swagger_ui_parameters: Annotated[
93 Optional[Dict[str, Any]],
94 Doc(
95 """
96 Configuration parameters for Swagger UI.
98 It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
99 """
100 ),
101 ] = None,
102) -> HTMLResponse:
103 """
104 Generate and return the HTML that loads Swagger UI for the interactive
105 API docs (normally served at `/docs`).
107 You would only call this function yourself if you needed to override some parts,
108 for example the URLs to use to load Swagger UI's JavaScript and CSS.
110 Read more about it in the
111 [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
112 and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
113 """
114 current_swagger_ui_parameters = swagger_ui_default_parameters.copy() 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
115 if swagger_ui_parameters: 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
116 current_swagger_ui_parameters.update(swagger_ui_parameters) 1ijkmnoqrsuvwyzACDEGHI
118 html = f""" 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
119 <!DOCTYPE html>
120 <html>
121 <head>
122 <link type="text/css" rel="stylesheet" href="{swagger_css_url}">
123 <link rel="shortcut icon" href="{swagger_favicon_url}">
124 <title>{title}</title>
125 </head>
126 <body>
127 <div id="swagger-ui">
128 </div>
129 <script src="{swagger_js_url}"></script>
130 <!-- `SwaggerUIBundle` is now available on the page -->
131 <script>
132 const ui = SwaggerUIBundle({{
133 url: '{openapi_url}',
134 """
136 for key, value in current_swagger_ui_parameters.items(): 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
137 html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n" 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
139 if oauth2_redirect_url: 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
140 html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," 1KLhaijkMNOPlbmnoQRSTpcqrsUVWXtduvwYZ01xeyzA2345BfCDE6789FgGHI!#
142 html += """ 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
143 presets: [
144 SwaggerUIBundle.presets.apis,
145 SwaggerUIBundle.SwaggerUIStandalonePreset
146 ],
147 })"""
149 if init_oauth: 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
150 html += f""" 1hlptxBF
151 ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
152 """
154 html += """ 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
155 </script>
156 </body>
157 </html>
158 """
159 return HTMLResponse(html) 2K L , - . h a i j k M N O P / : ; l b m n o Q R S T = ? @ p c q r s U V W X [ ] ^ t d u v w Y Z 0 1 _ ` { x e y z A 2 3 4 5 | } ~ B f C D E 6 7 8 9 abJ bbF g G H I ! #
162def get_redoc_html( 2$ % ' ( ) * + cb
163 *,
164 openapi_url: Annotated[
165 str,
166 Doc(
167 """
168 The OpenAPI URL that ReDoc should load and use.
170 This is normally done automatically by FastAPI using the default URL
171 `/openapi.json`.
172 """
173 ),
174 ],
175 title: Annotated[
176 str,
177 Doc(
178 """
179 The HTML `<title>` content, normally shown in the browser tab.
180 """
181 ),
182 ],
183 redoc_js_url: Annotated[
184 str,
185 Doc(
186 """
187 The URL to use to load the ReDoc JavaScript.
189 It is normally set to a CDN URL.
190 """
191 ),
192 ] = "https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js",
193 redoc_favicon_url: Annotated[
194 str,
195 Doc(
196 """
197 The URL of the favicon to use. It is normally shown in the browser tab.
198 """
199 ),
200 ] = "https://fastapi.tiangolo.com/img/favicon.png",
201 with_google_fonts: Annotated[
202 bool,
203 Doc(
204 """
205 Load and use Google Fonts.
206 """
207 ),
208 ] = True,
209) -> HTMLResponse:
210 """
211 Generate and return the HTML response that loads ReDoc for the alternative
212 API docs (normally served at `/redoc`).
214 You would only call this function yourself if you needed to override some parts,
215 for example the URLs to use to load ReDoc's JavaScript and CSS.
217 Read more about it in the
218 [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
219 """
220 html = f""" 2dbebfbgba hbibjbkblbmbb nbobpbqbrbsbc tbubvbwbxbybd zbAbBbCbDbEbe FbGbHbIbJbKbf LbMbNbObPbcbg QbRb
221 <!DOCTYPE html>
222 <html>
223 <head>
224 <title>{title}</title>
225 <!-- needed for adaptive design -->
226 <meta charset="utf-8"/>
227 <meta name="viewport" content="width=device-width, initial-scale=1">
228 """
229 if with_google_fonts: 2dbebfbgba hbibjbkblbmbb nbobpbqbrbsbc tbubvbwbxbybd zbAbBbCbDbEbe FbGbHbIbJbKbf LbMbNbObPbcbg QbRb
230 html += """ 2dbebfbgba hbibjbkblbmbb nbobpbqbrbsbc tbubvbwbxbybd zbAbBbCbDbEbe FbGbHbIbJbKbf LbMbNbObPbcbg QbRb
231 <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
232 """
233 html += f""" 2dbebfbgba hbibjbkblbmbb nbobpbqbrbsbc tbubvbwbxbybd zbAbBbCbDbEbe FbGbHbIbJbKbf LbMbNbObPbcbg QbRb
234 <link rel="shortcut icon" href="{redoc_favicon_url}">
235 <!--
236 ReDoc doesn't change outer page styles
237 -->
238 <style>
239 body {{
240 margin: 0;
241 padding: 0;
242 }}
243 </style>
244 </head>
245 <body>
246 <noscript>
247 ReDoc requires Javascript to function. Please enable it to browse the documentation.
248 </noscript>
249 <redoc spec-url="{openapi_url}"></redoc>
250 <script src="{redoc_js_url}"> </script>
251 </body>
252 </html>
253 """
254 return HTMLResponse(html) 2dbebfbgba hbibjbkblbmbb nbobpbqbrbsbc tbubvbwbxbybd zbAbBbCbDbEbe FbGbHbIbJbKbf LbMbNbObPbcbg QbRb
257def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: 1$%'()*+
258 """
259 Generate the HTML response with the OAuth2 redirection for Swagger UI.
261 You normally don't need to use or change this.
262 """
263 # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
264 html = """ 2SbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%b'b(b)b*b+b,b
265 <!doctype html>
266 <html lang="en-US">
267 <head>
268 <title>Swagger UI: OAuth2 Redirect</title>
269 </head>
270 <body>
271 <script>
272 'use strict';
273 function run () {
274 var oauth2 = window.opener.swaggerUIRedirectOauth2;
275 var sentState = oauth2.state;
276 var redirectUrl = oauth2.redirectUrl;
277 var isValid, qp, arr;
279 if (/code|token|error/.test(window.location.hash)) {
280 qp = window.location.hash.substring(1).replace('?', '&');
281 } else {
282 qp = location.search.substring(1);
283 }
285 arr = qp.split("&");
286 arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
287 qp = qp ? JSON.parse('{' + arr.join() + '}',
288 function (key, value) {
289 return key === "" ? value : decodeURIComponent(value);
290 }
291 ) : {};
293 isValid = qp.state === sentState;
295 if ((
296 oauth2.auth.schema.get("flow") === "accessCode" ||
297 oauth2.auth.schema.get("flow") === "authorizationCode" ||
298 oauth2.auth.schema.get("flow") === "authorization_code"
299 ) && !oauth2.auth.code) {
300 if (!isValid) {
301 oauth2.errCb({
302 authId: oauth2.auth.name,
303 source: "auth",
304 level: "warning",
305 message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
306 });
307 }
309 if (qp.code) {
310 delete oauth2.state;
311 oauth2.auth.code = qp.code;
312 oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
313 } else {
314 let oauthErrorMsg;
315 if (qp.error) {
316 oauthErrorMsg = "["+qp.error+"]: " +
317 (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
318 (qp.error_uri ? "More info: "+qp.error_uri : "");
319 }
321 oauth2.errCb({
322 authId: oauth2.auth.name,
323 source: "auth",
324 level: "error",
325 message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
326 });
327 }
328 } else {
329 oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
330 }
331 window.close();
332 }
334 if (document.readyState !== 'loading') {
335 run();
336 } else {
337 document.addEventListener('DOMContentLoaded', function () {
338 run();
339 });
340 }
341 </script>
342 </body>
343 </html>
344 """
345 return HTMLResponse(content=html) 2SbTbUbVbWbXbYbZb0b1b2b3b4b5b6b7b8b9b!b#b$b%b'b(b)b*b+b,b