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

1import json 1abcde

2from typing import Any, Dict, Optional 1abcde

3 

4from fastapi.encoders import jsonable_encoder 1abcde

5from starlette.responses import HTMLResponse 1abcde

6from typing_extensions import Annotated, Doc 1abcde

7 

8swagger_ui_default_parameters: Annotated[ 1abcde

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( 1abcde

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 ), 

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. 

52 

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. 

62 

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. 

96 

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

105 

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. 

108 

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

116 

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

134 

135 for key, value in current_swagger_ui_parameters.items(): 1abcde

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

137 

138 if oauth2_redirect_url: 1abcde

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

140 

141 html += """ 1abcde

142 presets: [ 

143 SwaggerUIBundle.presets.apis, 

144 SwaggerUIBundle.SwaggerUIStandalonePreset 

145 ], 

146 })""" 

147 

148 if init_oauth: 1abcde

149 html += f""" 1abcde

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

151 """ 

152 

153 html += """ 1abcde

154 </script> 

155 </body> 

156 </html> 

157 """ 

158 return HTMLResponse(html) 1abcde

159 

160 

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. 

168 

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. 

187 

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

212 

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. 

215 

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

254 

255 

256def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: 1abcde

257 """ 

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

259 

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; 

277 

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 } 

283 

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

291 

292 isValid = qp.state === sentState; 

293 

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 } 

307 

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 } 

319 

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 } 

332 

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