Coverage for fastapi/openapi/docs.py: 100%
29 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-08-08 03:53 +0000
1import json 1abcde
2from typing import Any, Dict, Optional 1abcde
4from fastapi.encoders import jsonable_encoder 1abcde
5from starlette.responses import HTMLResponse 1abcde
6from typing_extensions import Annotated, Doc 1abcde
8swagger_ui_default_parameters: Annotated[ 1abcde
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( 1abcde
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`.
36 """
37 ),
38 ],
39 title: Annotated[
40 str,
41 Doc(
42 """
43 The HTML `<title>` content, normally shown in the browser tab.
44 """
45 ),
46 ],
47 swagger_js_url: Annotated[
48 str,
49 Doc(
50 """
51 The URL to use to load the Swagger UI JavaScript.
53 It is normally set to a CDN URL.
54 """
55 ),
56 ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
57 swagger_css_url: Annotated[
58 str,
59 Doc(
60 """
61 The URL to use to load the Swagger UI CSS.
63 It is normally set to a CDN URL.
64 """
65 ),
66 ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
67 swagger_favicon_url: Annotated[
68 str,
69 Doc(
70 """
71 The URL of the favicon to use. It is normally shown in the browser tab.
72 """
73 ),
74 ] = "https://fastapi.tiangolo.com/img/favicon.png",
75 oauth2_redirect_url: Annotated[
76 Optional[str],
77 Doc(
78 """
79 The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
80 """
81 ),
82 ] = None,
83 init_oauth: Annotated[
84 Optional[Dict[str, Any]],
85 Doc(
86 """
87 A dictionary with Swagger UI OAuth2 initialization configurations.
88 """
89 ),
90 ] = None,
91 swagger_ui_parameters: Annotated[
92 Optional[Dict[str, Any]],
93 Doc(
94 """
95 Configuration parameters for Swagger UI.
97 It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters].
98 """
99 ),
100 ] = None,
101) -> HTMLResponse:
102 """
103 Generate and return the HTML that loads Swagger UI for the interactive
104 API docs (normally served at `/docs`).
106 You would only call this function yourself if you needed to override some parts,
107 for example the URLs to use to load Swagger UI's JavaScript and CSS.
109 Read more about it in the
110 [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/)
111 and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
112 """
113 current_swagger_ui_parameters = swagger_ui_default_parameters.copy() 1abcde
114 if swagger_ui_parameters: 1abcde
115 current_swagger_ui_parameters.update(swagger_ui_parameters) 1abcde
117 html = f""" 1abcde
118 <!DOCTYPE html>
119 <html>
120 <head>
121 <link type="text/css" rel="stylesheet" href="{swagger_css_url}">
122 <link rel="shortcut icon" href="{swagger_favicon_url}">
123 <title>{title}</title>
124 </head>
125 <body>
126 <div id="swagger-ui">
127 </div>
128 <script src="{swagger_js_url}"></script>
129 <!-- `SwaggerUIBundle` is now available on the page -->
130 <script>
131 const ui = SwaggerUIBundle({{
132 url: '{openapi_url}',
133 """
135 for key, value in current_swagger_ui_parameters.items(): 1abcde
136 html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n" 1abcde
138 if oauth2_redirect_url: 1abcde
139 html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," 1abcde
141 html += """ 1abcde
142 presets: [
143 SwaggerUIBundle.presets.apis,
144 SwaggerUIBundle.SwaggerUIStandalonePreset
145 ],
146 })"""
148 if init_oauth: 1abcde
149 html += f""" 1abcde
150 ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))})
151 """
153 html += """ 1abcde
154 </script>
155 </body>
156 </html>
157 """
158 return HTMLResponse(html) 1abcde
161def get_redoc_html( 1abcde
162 *,
163 openapi_url: Annotated[
164 str,
165 Doc(
166 """
167 The OpenAPI URL that ReDoc should load and use.
169 This is normally done automatically by FastAPI using the default URL
170 `/openapi.json`.
171 """
172 ),
173 ],
174 title: Annotated[
175 str,
176 Doc(
177 """
178 The HTML `<title>` content, normally shown in the browser tab.
179 """
180 ),
181 ],
182 redoc_js_url: Annotated[
183 str,
184 Doc(
185 """
186 The URL to use to load the ReDoc JavaScript.
188 It is normally set to a CDN URL.
189 """
190 ),
191 ] = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
192 redoc_favicon_url: Annotated[
193 str,
194 Doc(
195 """
196 The URL of the favicon to use. It is normally shown in the browser tab.
197 """
198 ),
199 ] = "https://fastapi.tiangolo.com/img/favicon.png",
200 with_google_fonts: Annotated[
201 bool,
202 Doc(
203 """
204 Load and use Google Fonts.
205 """
206 ),
207 ] = True,
208) -> HTMLResponse:
209 """
210 Generate and return the HTML response that loads ReDoc for the alternative
211 API docs (normally served at `/redoc`).
213 You would only call this function yourself if you needed to override some parts,
214 for example the URLs to use to load ReDoc's JavaScript and CSS.
216 Read more about it in the
217 [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/).
218 """
219 html = f""" 1abcde
220 <!DOCTYPE html>
221 <html>
222 <head>
223 <title>{title}</title>
224 <!-- needed for adaptive design -->
225 <meta charset="utf-8"/>
226 <meta name="viewport" content="width=device-width, initial-scale=1">
227 """
228 if with_google_fonts: 1abcde
229 html += """ 1abcde
230 <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
231 """
232 html += f""" 1abcde
233 <link rel="shortcut icon" href="{redoc_favicon_url}">
234 <!--
235 ReDoc doesn't change outer page styles
236 -->
237 <style>
238 body {{
239 margin: 0;
240 padding: 0;
241 }}
242 </style>
243 </head>
244 <body>
245 <noscript>
246 ReDoc requires Javascript to function. Please enable it to browse the documentation.
247 </noscript>
248 <redoc spec-url="{openapi_url}"></redoc>
249 <script src="{redoc_js_url}"> </script>
250 </body>
251 </html>
252 """
253 return HTMLResponse(html) 1abcde
256def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: 1abcde
257 """
258 Generate the HTML response with the OAuth2 redirection for Swagger UI.
260 You normally don't need to use or change this.
261 """
262 # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html
263 html = """ 1abcde
264 <!doctype html>
265 <html lang="en-US">
266 <head>
267 <title>Swagger UI: OAuth2 Redirect</title>
268 </head>
269 <body>
270 <script>
271 'use strict';
272 function run () {
273 var oauth2 = window.opener.swaggerUIRedirectOauth2;
274 var sentState = oauth2.state;
275 var redirectUrl = oauth2.redirectUrl;
276 var isValid, qp, arr;
278 if (/code|token|error/.test(window.location.hash)) {
279 qp = window.location.hash.substring(1).replace('?', '&');
280 } else {
281 qp = location.search.substring(1);
282 }
284 arr = qp.split("&");
285 arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
286 qp = qp ? JSON.parse('{' + arr.join() + '}',
287 function (key, value) {
288 return key === "" ? value : decodeURIComponent(value);
289 }
290 ) : {};
292 isValid = qp.state === sentState;
294 if ((
295 oauth2.auth.schema.get("flow") === "accessCode" ||
296 oauth2.auth.schema.get("flow") === "authorizationCode" ||
297 oauth2.auth.schema.get("flow") === "authorization_code"
298 ) && !oauth2.auth.code) {
299 if (!isValid) {
300 oauth2.errCb({
301 authId: oauth2.auth.name,
302 source: "auth",
303 level: "warning",
304 message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
305 });
306 }
308 if (qp.code) {
309 delete oauth2.state;
310 oauth2.auth.code = qp.code;
311 oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
312 } else {
313 let oauthErrorMsg;
314 if (qp.error) {
315 oauthErrorMsg = "["+qp.error+"]: " +
316 (qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
317 (qp.error_uri ? "More info: "+qp.error_uri : "");
318 }
320 oauth2.errCb({
321 authId: oauth2.auth.name,
322 source: "auth",
323 level: "error",
324 message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
325 });
326 }
327 } else {
328 oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
329 }
330 window.close();
331 }
333 if (document.readyState !== 'loading') {
334 run();
335 } else {
336 document.addEventListener('DOMContentLoaded', function () {
337 run();
338 });
339 }
340 </script>
341 </body>
342 </html>
343 """
344 return HTMLResponse(content=html) 1abcde