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

1import json 1NOPQ

2from typing import Annotated, Any 1NOPQ

3 

4from annotated_doc import Doc 1NOPQ

5from fastapi.encoders import jsonable_encoder 1NOPQ

6from starlette.responses import HTMLResponse 1NOPQ

7 

8swagger_ui_default_parameters: Annotated[ 1NOPQ

9 dict[str, Any], 

10 Doc( 

11 """ 

12 Default configurations for Swagger UI. 

13 

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} 

24 

25 

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. 

33 

34 This is normally done automatically by FastAPI using the default URL 

35 `/openapi.json`. 

36 

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. 

47 

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. 

58 

59 It is normally set to a CDN URL. 

60 

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. 

71 

72 It is normally set to a CDN URL. 

73 

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. 

92 

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. 

103 

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. 

114 

115 It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters]. 

116 

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`). 

126 

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. 

129 

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

137 

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 """ 

156 

157 for key, value in current_swagger_ui_parameters.items(): 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

158 html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n" 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

159 

160 if oauth2_redirect_url: 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

161 html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," 1qrdaefgstuvwhbijkxyzABlcmnoCDE

162 

163 html += """ 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

164 presets: [ 

165 SwaggerUIBundle.presets.apis, 

166 SwaggerUIBundle.SwaggerUIStandalonePreset 

167 ], 

168 })""" 

169 

170 if init_oauth: 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

171 html += f""" 1dhl

172 ui.initOAuth({json.dumps(jsonable_encoder(init_oauth))}) 

173 """ 

174 

175 html += """ 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

176 </script> 

177 </body> 

178 </html> 

179 """ 

180 return HTMLResponse(html) 1qrFGHdaefgstuvwIpJhbijkxyzABKLMlcmnoCDE

181 

182 

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. 

190 

191 This is normally done automatically by FastAPI using the default URL 

192 `/openapi.json`. 

193 

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. 

204 

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. 

215 

216 It is normally set to a CDN URL. 

217 

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`). 

243 

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. 

246 

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

285 

286 

287def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: 1NOPQ

288 """ 

289 Generate the HTML response with the OAuth2 redirection for Swagger UI. 

290 

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; 

308 

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 } 

314 

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 ) : {}; 

322 

323 isValid = qp.state === sentState; 

324 

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 } 

338 

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 } 

350 

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 } 

363 

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!#$%'()*+,-