Coverage for fastapi / openapi / docs.py: 100%
29 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
1import json 1NOPQ
2from typing import Annotated, Any 1NOPQ
4from annotated_doc import Doc 1NOPQ
5from fastapi.encoders import jsonable_encoder 1NOPQ
6from starlette.responses import HTMLResponse 1NOPQ
8swagger_ui_default_parameters: Annotated[ 1NOPQ
9 dict[str, Any],
10 Doc(
11 """
12 Default configurations for Swagger UI.
14 You can use it as a template to add any other configurations needed.
15 """
16 ),
17] = {
18 "dom_id": "#swagger-ui",
19 "layout": "BaseLayout",
20 "deepLinking": True,
21 "showExtensions": True,
22 "showCommonExtensions": True,
23}
26def get_swagger_ui_html( 1NOpPQ
27 *,
28 openapi_url: Annotated[
29 str,
30 Doc(
31 """
32 The OpenAPI URL that Swagger UI should load and use.
34 This is normally done automatically by FastAPI using the default URL
35 `/openapi.json`.
37 Read more about it in the
38 [FastAPI docs for Conditional OpenAPI](https://fastapi.tiangolo.com/how-to/conditional-openapi/#conditional-openapi-from-settings-and-env-vars)
39 """
40 ),
41 ],
42 title: Annotated[
43 str,
44 Doc(
45 """
46 The HTML `<title>` content, normally shown in the browser tab.
48 Read more about it in the
49 [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
50 """
51 ),
52 ],
53 swagger_js_url: Annotated[
54 str,
55 Doc(
56 """
57 The URL to use to load the Swagger UI JavaScript.
59 It is normally set to a CDN URL.
61 Read more about it in the
62 [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
63 """
64 ),
65 ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
66 swagger_css_url: Annotated[
67 str,
68 Doc(
69 """
70 The URL to use to load the Swagger UI CSS.
72 It is normally set to a CDN URL.
74 Read more about it in the
75 [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
76 """
77 ),
78 ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
79 swagger_favicon_url: Annotated[
80 str,
81 Doc(
82 """
83 The URL of the favicon to use. It is normally shown in the browser tab.
84 """
85 ),
86 ] = "https://fastapi.tiangolo.com/img/favicon.png",
87 oauth2_redirect_url: Annotated[
88 str | None,
89 Doc(
90 """
91 The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
93 Read more about it in the
94 [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
95 """
96 ),
97 ] = None,
98 init_oauth: Annotated[
99 dict[str, Any] | None,
100 Doc(
101 """
102 A dictionary with Swagger UI OAuth2 initialization configurations.
104 Read more about the available configuration options in the
105 [Swagger UI docs](https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/).
106 """
107 ),
108 ] = None,
109 swagger_ui_parameters: Annotated[
110 dict[str, Any] | None,
111 Doc(
112 """
113 Configuration parameters for Swagger UI.
115 It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
117 Read more about it in the
118 [FastAPI docs about how to Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/).
119 """
120 ),
121 ] = None,
122) -> HTMLResponse:
123 """
124 Generate and return the HTML that loads Swagger UI for the interactive
125 API docs (normally served at `/docs`).
127 You would only call this function yourself if you needed to override some parts,
128 for example the URLs to use to load Swagger UI's JavaScript and CSS.
130 Read more about it in the
131 [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
132 and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
133 """
134 current_swagger_ui_parameters = swagger_ui_default_parameters.copy() 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
135 if swagger_ui_parameters: 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
136 current_swagger_ui_parameters.update(swagger_ui_parameters) 1efgijkmno
138 html = f""" 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
139 <!DOCTYPE html>
140 <html>
141 <head>
142 <meta name="viewport" content="width=device-width, initial-scale=1.0">
143 <link type="text/css" rel="stylesheet" href="{swagger_css_url}">
144 <link rel="shortcut icon" href="{swagger_favicon_url}">
145 <title>{title}</title>
146 </head>
147 <body>
148 <div id="swagger-ui">
149 </div>
150 <script src="{swagger_js_url}"></script>
151 <!-- `SwaggerUIBundle` is now available on the page -->
152 <script>
153 const ui = SwaggerUIBundle({{
154 url: '{openapi_url}',
155 """
157 for key, value in current_swagger_ui_parameters.items(): 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
158 html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n" 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
160 if oauth2_redirect_url: 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
161 html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," 1qrdaefgstuvwhbijkxyzABlcmnoCDE
163 html += """ 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
164 presets: [
165 SwaggerUIBundle.presets.apis,
166 SwaggerUIBundle.SwaggerUIStandalonePreset
167 ],
168 })"""
170 if init_oauth: 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
171 html += f""" 1dhl
172 ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
173 """
175 html += """ 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
176 </script>
177 </body>
178 </html>
179 """
180 return HTMLResponse(html) 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE
183def get_redoc_html( 1NORPQ
184 *,
185 openapi_url: Annotated[
186 str,
187 Doc(
188 """
189 The OpenAPI URL that ReDoc should load and use.
191 This is normally done automatically by FastAPI using the default URL
192 `/openapi.json`.
194 Read more about it in the
195 [FastAPI docs for Conditional OpenAPI](https://fastapi.tiangolo.com/how-to/conditional-openapi/#conditional-openapi-from-settings-and-env-vars)
196 """
197 ),
198 ],
199 title: Annotated[
200 str,
201 Doc(
202 """
203 The HTML `<title>` content, normally shown in the browser tab.
205 Read more about it in the
206 [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
207 """
208 ),
209 ],
210 redoc_js_url: Annotated[
211 str,
212 Doc(
213 """
214 The URL to use to load the ReDoc JavaScript.
216 It is normally set to a CDN URL.
218 Read more about it in the
219 [FastAPI docs for Custom Docs UI Static Assets](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/)
220 """
221 ),
222 ] = "https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js",
223 redoc_favicon_url: Annotated[
224 str,
225 Doc(
226 """
227 The URL of the favicon to use. It is normally shown in the browser tab.
228 """
229 ),
230 ] = "https://fastapi.tiangolo.com/img/favicon.png",
231 with_google_fonts: Annotated[
232 bool,
233 Doc(
234 """
235 Load and use Google Fonts.
236 """
237 ),
238 ] = True,
239) -> HTMLResponse:
240 """
241 Generate and return the HTML response that loads ReDoc for the alternative
242 API docs (normally served at `/redoc`).
244 You would only call this function yourself if you needed to override some parts,
245 for example the URLs to use to load ReDoc's JavaScript and CSS.
247 Read more about it in the
248 [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
249 """
250 html = f""" 1STUVaWXYZ0Rb123456c78
251 <!DOCTYPE html>
252 <html>
253 <head>
254 <title>{title}</title>
255 <!-- needed for adaptive design -->
256 <meta charset="utf-8"/>
257 <meta name="viewport" content="width=device-width, initial-scale=1">
258 """
259 if with_google_fonts: 1STUVaWXYZ0Rb123456c78
260 html += """ 1STUVaWXYZ0Rb123456c78
261 <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
262 """
263 html += f""" 1STUVaWXYZ0Rb123456c78
264 <link rel="shortcut icon" href="{redoc_favicon_url}">
265 <!--
266 ReDoc doesn't change outer page styles
267 -->
268 <style>
269 body {{
270 margin: 0;
271 padding: 0;
272 }}
273 </style>
274 </head>
275 <body>
276 <noscript>
277 ReDoc requires Javascript to function. Please enable it to browse the documentation.
278 </noscript>
279 <redoc spec-url="{openapi_url}"></redoc>
280 <script src="{redoc_js_url}"> </script>
281 </body>
282 </html>
283 """
284 return HTMLResponse(html) 1STUVaWXYZ0Rb123456c78
287def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: 1NOPQ
288 """
289 Generate the HTML response with the OAuth2 redirection for Swagger UI.
291 You normally don't need to use or change this.
292 """
293 # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
294 html = """ 19!#$%'()*+,-
295 <!doctype html>
296 <html lang="en-US">
297 <head>
298 <title>Swagger UI: OAuth2 Redirect</title>
299 </head>
300 <body>
301 <script>
302 'use strict';
303 function run () {
304 var oauth2 = window.opener.swaggerUIRedirectOauth2;
305 var sentState = oauth2.state;
306 var redirectUrl = oauth2.redirectUrl;
307 var isValid, qp, arr;
309 if (/code|token|error/.test(window.location.hash)) {
310 qp = window.location.hash.substring(1).replace('?', '&');
311 } else {
312 qp = location.search.substring(1);
313 }
315 arr = qp.split("&");
316 arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
317 qp = qp ? JSON.parse('{' + arr.join() + '}',
318 function (key, value) {
319 return key === "" ? value : decodeURIComponent(value);
320 }
321 ) : {};
323 isValid = qp.state === sentState;
325 if ((
326 oauth2.auth.schema.get("flow") === "accessCode" ||
327 oauth2.auth.schema.get("flow") === "authorizationCode" ||
328 oauth2.auth.schema.get("flow") === "authorization_code"
329 ) && !oauth2.auth.code) {
330 if (!isValid) {
331 oauth2.errCb({
332 authId: oauth2.auth.name,
333 source: "auth",
334 level: "warning",
335 message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
336 });
337 }
339 if (qp.code) {
340 delete oauth2.state;
341 oauth2.auth.code = qp.code;
342 oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
343 } else {
344 let oauthErrorMsg;
345 if (qp.error) {
346 oauthErrorMsg = "["+qp.error+"]: " +
347 (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
348 (qp.error_uri ? "More info: "+qp.error_uri : "");
349 }
351 oauth2.errCb({
352 authId: oauth2.auth.name,
353 source: "auth",
354 level: "error",
355 message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
356 });
357 }
358 } else {
359 oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
360 }
361 window.close();
362 }
364 if (document.readyState !== 'loading') {
365 run();
366 } else {
367 document.addEventListener('DOMContentLoaded', function () {
368 run();
369 });
370 }
371 </script>
372 </body>
373 </html>
374 """
375 return HTMLResponse(content=html) 19!#$%'()*+,-