From a85aa125d24acb225b7b722bb4f3e331a21ec267 Mon Sep 17 00:00:00 2001 From: John Riebold Date: Sun, 16 Jan 2022 11:26:24 -0800 Subject: [PATCH] =?utf8?q?=E2=9C=A8=20Enable=20configuring=20Swagger=20UI?= =?utf8?q?=20parameters=20(#2568)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-authored-by: Artem Ivanov Co-authored-by: Sebastián Ramírez --- docs/en/docs/advanced/extending-openapi.md | 79 ++++++++++++++++++ .../tutorial/extending-openapi/image02.png | Bin 0 -> 11292 bytes .../tutorial/extending-openapi/image03.png | Bin 0 -> 11176 bytes .../tutorial/extending-openapi/image04.png | Bin 0 -> 11180 bytes docs_src/extending_openapi/tutorial003.py | 8 ++ docs_src/extending_openapi/tutorial004.py | 8 ++ docs_src/extending_openapi/tutorial005.py | 8 ++ fastapi/applications.py | 3 + fastapi/openapi/docs.py | 22 +++-- .../test_tutorial003.py | 41 +++++++++ .../test_tutorial004.py | 44 ++++++++++ .../test_tutorial005.py | 44 ++++++++++ 12 files changed, 251 insertions(+), 6 deletions(-) create mode 100644 docs/en/docs/img/tutorial/extending-openapi/image02.png create mode 100644 docs/en/docs/img/tutorial/extending-openapi/image03.png create mode 100644 docs/en/docs/img/tutorial/extending-openapi/image04.png create mode 100644 docs_src/extending_openapi/tutorial003.py create mode 100644 docs_src/extending_openapi/tutorial004.py create mode 100644 docs_src/extending_openapi/tutorial005.py create mode 100644 tests/test_tutorial/test_extending_openapi/test_tutorial003.py create mode 100644 tests/test_tutorial/test_extending_openapi/test_tutorial004.py create mode 100644 tests/test_tutorial/test_extending_openapi/test_tutorial005.py diff --git a/docs/en/docs/advanced/extending-openapi.md b/docs/en/docs/advanced/extending-openapi.md index d8d280ba67..d1b14bc003 100644 --- a/docs/en/docs/advanced/extending-openapi.md +++ b/docs/en/docs/advanced/extending-openapi.md @@ -233,3 +233,82 @@ Now, to be able to test that everything works, create a *path operation*: Now, you should be able to disconnect your WiFi, go to your docs at http://127.0.0.1:8000/docs, and reload the page. And even without Internet, you would be able to see the docs for your API and interact with it. + +## Configuring Swagger UI + +You can configure some extra Swagger UI parameters. + +To configure them, pass the `swagger_ui_parameters` argument when creating the `FastAPI()` app object or to the `get_swagger_ui_html()` function. + +`swagger_ui_parameters` receives a dictionary with the configurations passed to Swagger UI directly. + +FastAPI converts the configurations to **JSON** to make them compatible with JavaScript, as that's what Swagger UI needs. + +### Disable Syntax Highlighting + +For example, you could disable syntax highlighting in Swagger UI. + +Without changing the settings, syntax highlighting is enabled by default: + + + +But you can disable it by setting `syntaxHighlight` to `False`: + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial003.py!} +``` + +...and then Swagger UI won't show the syntax highlighting anymore: + + + +### Change the Theme + +The same way you could set the syntax highlighting theme with the key `"syntaxHighlight.theme"` (notice that it has a dot in the middle): + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial004.py!} +``` + +That configuration would change the syntax highlighting color theme: + + + +### Change Default Swagger UI Parameters + +FastAPI includes some default configuration parameters appropriate for most of the use cases. + +It includes these default configurations: + +```Python +{!../../../fastapi/openapi/docs.py[ln:7-13]!} +``` + +You can override any of them by setting a different value in the argument `swagger_ui_parameters`. + +For example, to disable `deepLinking` you could pass these settings to `swagger_ui_parameters`: + +```Python hl_lines="3" +{!../../../docs_src/extending_openapi/tutorial005.py!} +``` + +### Other Swagger UI Parameters + +To see all the other possible configurations you can use, read the official docs for Swagger UI parameters. + +### JavaScript-only settings + +Swagger UI also allows other configurations to be **JavaScript-only** objects (for example, JavaScript functions). + +FastAPI also includes these JavaScript-only `presets` settings: + +```JavaScript +presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIBundle.SwaggerUIStandalonePreset +] +``` + +These are **JavaScript** objects, not strings, so you can't pass them from Python code directly. + +If you need to use JavaScript-only configurations like those, you can use one of the methods above. Override all the Swagger UI *path operation* and manually write any JavaScript you need. diff --git a/docs/en/docs/img/tutorial/extending-openapi/image02.png b/docs/en/docs/img/tutorial/extending-openapi/image02.png new file mode 100644 index 0000000000000000000000000000000000000000..91453fb56f687b99fc725afa0a7fa07eb1955137 GIT binary patch literal 11292 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 literal 0 Hc-jL100001 diff --git a/docs/en/docs/img/tutorial/extending-openapi/image04.png b/docs/en/docs/img/tutorial/extending-openapi/image04.png new file mode 100644 index 0000000000000000000000000000000000000000..394d2bb431ab5eadaa919087ce1618f996bd829e GIT binary patch literal 11180 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ℓGjSAG%F<(t#w~BfjFZW(&)H@ z8wdE7q$Ioly1AQ)eh45Vo8sX5H(e|ItZsjujFn^-22~1rU=}F|3>V4BK0o~u@A)PX zbksrFs78si6nDU+NDBC0K7YEV-M%Tdt1j2N?tXQ}_DLFFAc=nyN9X*YGBi$%vO)8o z@#tZ&0D(qfka{>PFffqeREAvwopatR!`A8m|I^}K%xrJ}8w;etRgx;+?NOfOxe9xt z?Au#x7X6P99xMU2{N(3O?SL<$Ie*fbJy*Jq zvveahoz#qP)SOuv|HY*U?51qCiVfZQSTCPf@54rVOy1fg^0Ed_uEnlB)YTOoQ_W94 z(Li%Hz`z8#<40djxtKVeVXkn~)+=P|Y0=^zZt(L|mNU@R3k(@~?A}wx+Om7L2lZCW zcbX3BxI5+D4oWri2;w_P@I*%g1sU1gOZk@ihwE7-ZNYjchvQY_k8h`QAUM}fnAh~Y z%u!iNncdzUr)!HP&Kcx2eG?BFr1{`&)5eNJrT2w~5>ttx;Ladx{6ykW3X%hEhbvJy znGcowR>I5Ok1tR!RlA%5S8y5<-AyU#P#!6_)?}<2>EEl!IKdQ+Z#T2RI6)FEo;o^e z-B$Y(QPGjzcP_7(v}H;`*B5>(gh&l^m7G7d+tY$CI~>%G%uCl`^Q--Dqw4Wj*amKn z1q7j>FFTYKDPmLjxPO}1T7?BV(cw1HpbC`dnEUrH3mp!}Sf8W)Fh%!0WNj$d(p7&4 z^n#FK3rx&aE`l zj>|m82lM?v_8e?fIsraNx(EAK@)yC6?Q~buKf=iUifCK|eb|Tx(iV^NonAml>ZMe< zBKi+Q z;NfaAY$JFQ4b71Huyy@2%qygIpicCvKsM|w2Nfc5OeQl3HvxU$&-{Vr6Q`xcRjN$H z&5s;M>z%>c3-dw9+srXhntcTpzly^bj`k-;ZiM0{%&=U7&OyX90!R5b4KH~$Dd_{3 zp}k$`bezQ?ZKXYjd{ALj73pjf{DmFowI_~fIYkU|gOv!+alynBr@VeW>6);o@PR_~ z(qNHx-k5=oc{O~N5S&Sw1^*_@gIc~?Lx8-(Plk@KA+(v*1{e!CaCP*mqy$I z#xZr1p`B~e7t}Tr-Zd0p*XsPD`eUP7(}V7Guasc}+w)HF*f5{%_dVq%Glw;sAWa+dW?TE|GZr&eCVcL1Zb{+dj<%JNUt9xP#p@7kQ^$=uRiem4x!LF zqkHb_s9WgFN$S3Fi7kt-%Xkd;(-hO_Z;l<2#PU+2WNFbkGJP+%@#X~XBo%B8a^!l` zPa6r=xEMoPW-5u9*vlG4_OQT((fds508^D~B_sezqI)SOcmq70-mXf*GP2){%ys9^ zVma503HNxjoKNc(W9iI%oX3=&%4t!f%{cNmH+(OS&NrGC)AX$8n~0;~>S=(#8Zw>N zDtlw_*2hr0;g&qdVp2pYex0_gZ+1}ts-;%xy?^O6&c2~DI%e}NW-gcC-rC|nmB~t5 zo;u2!Ph)Ng+hod{k*oHdPVqS%(OCB#31rZf;OVqf?8rw47ys_>Z1)8&ordEd8qRw= zJi$zzYj;bZaC1ZT>COfht7^)k1&G|sVK`d@Xnw(0K~0q>_k0QY*;A1wip{nwT{gt^ zywr9C%TEZ2DczfEgVH9y!FYvqMI8+dK^dJ5E}unrl6+AEHS!j-K1oe|0SWXe3MZc@ zp*IIJF8n6go$ZkCB+LF@u}5oyqA* zA%du`K+?S73`QLKdq|^QB!C$D*`iy&V5%K?d!SJ4p)dm=ft6!>Fr2fgGAwEN_uXcn zX+@*Q`GH|RY%$?>vq95uXIAsHoJAjAJ|zwZO@kA4c0(Fre3#joS>fqslIAp-M}J__ z{rlx+1|gFo62vg|uIq%oLYvTbdRx#6z{J}|W3}?-=b)NQ6UhqAs*@pyNf%!%bH_fN zwz>F0I?6|Rkn7a{o3awTYfm$LwPx#rkTlH-gF6ltnLB%b+GIkz-qoLjc{JU8wzj@P z;ahC|D=yq~XFM}T8el*iaX5FXSn)IH8w&o9L3p>yDE7OFOYa+VOwKny)HI3B)l&-l zQ6A0B?L9YIPtiF&N;fH=>2jVd0?x@?Hw`To^%Bg5LW8FH%mN?lsJaaA(0_2KQPr{O zRy55Y*z$4D;NxzrRcU~(2@=p2nBH2QSxp{kl1sxy9)x(AoWG-6s8FLTu}`a)pk$Kt z6}(2s9&!VY=XnkOhabpw(_?83>JrMZE;0|(p+(fx% zyA53EDLAAykzR!f@|(Qry0!<#^p!$D>p5lDpp>Mee_nl{`i_#$fK+O?ebp$w{YVT~ zV%T|Saj=$A{eVXl(|}0F&u!no3)EF7BO8u!Z`wC_eP{81FGvt51$J_*fcUCemJ*KghN5wvX&A0|3Md z{?Z-&57rHy(s$}MWMi~-?XN%J?1M-zgbtu5M%V;%{ajrY`9FsBPw;iEFf9Cv-?Mg^ z0;dn@8Wf6GSa0fXuhbq3DCbA&GJa^kxE`XV z0NPNzB>8SfKJO<#wiQ7gB)(2Q-|O<#din`U<$u}c z#=#7#7uR!XM1Y%{nT6jsdeLip>v;^Is>4>**;?_s!!n-_YLWB&oE3uGZL2F z+a z_Bt}Sxo4^mQNZwoKd~fIN+#-Y|FFOP&UW#3F;e17`u=SBx`|Fr5L|Zm-sW=uQ&U-B zYbk?ey>qN!E{oEO3`665JE56sR&PdL`Lq0k@IZjYgwmtdHx{hdB@pv&>&l)G#Chrf zMvYdozfu-sv7ldl4=AFkwhJ~zGsKWWa(P4vEx%q2LxRg3?Qe;7ux5QOJz(LDocW?5 zdu~mciPFgGSEh{AG0|)zlh|g^7n>^*X=+r(%k09UrHJaDpoUQS;~8pgR#RDB@5alj zX#O}ouQQGXPau@dUmyD}-uvu%{W~C}7f~0i1^`srlMDZ`384888EFf=a;!DNOYF`HDFwGF16K^fNauH)K}V>Y zD??hxr{SqgCQR=eaqE+ypYSVPcMF5p-m~EKOwlldB9Y1X01i^BqTl)-@z#fxW%H+W znR;`UsmO~a>PZvsD{;+lv%P(3*8|Vu9VyppDjeFmQs%*s8YDit5tL$4|AXcXM}t2~ z{y1_to(VQ2rF;kOPcJ2C%ONCCqM%9_`-$axFn;mU$G;d|ps*K4*cq7h|6@ejA)Ul6=P17A5u{^2Ps|T*k(9mBw<4OZp?ecAX%0Sk5q1&&UMe)mVRu znDsK1@nWLWQk~!Gr+!*7saNlS2or#-mUS7(hS0VS8!?l=lziK`6BB6riGV5Qw*ju8 zMG42g-7<*kYDSQoaPEV@Q2cqcD)CwsaiKG=w&iH*F_e@Tm0S00l7F-2^^}8OK1*M* z4p&eo<*}~*7va=rL^k~vUD_7~kNGIpd1{*bzd##c+KuS74B-Fx%%NJ>!A)IXqy6<}R+hJD2ot18VHOYv{Z(eK`y zs}zMP0me#n?93-tP$A#C=_j83`pblKN09cn#e#67a~kSbQZPJNPRyZ#=U8L&&&ft9 zoy_gSq4WgorgXr)3%a_jLj2<9yE8-Cmx!AxZ_;s1sMS?uYMaA zyC?Ob@pjWn%tzPNBT_~#r{P~sXLJGHUF{!p=S?6@EI0#HH>bCBQ`r|9b&EHRGCx>d zt&b78S`HTg#SJim>BNhQO7z%2RK*mhywrm2NbpK;mW@9#b@m>DBW}W86V5{PaB;>_ zN)P@QPVL34Y8wy5CN#wbnGYjZ#w{9t@zyppPS}I2AT6Lf*?y=v>tI{plT_j1nnk-E zBXw_A-|!RQWM)c!ho_$BNKM|-!BhGNDAe0v5 z=9KFF_VK35SyAG96DkZWCeJ=iRA0g+qfxLg^O_r*;V?D{mYqE9t>7#F&>1*vtXFrB z_)}wooZz2Y%w71C3OQ-wo|h{OAA>MK)95n8ip@eyMNXy?mjfI(qvkQ|gp_v4^_S1} z96gtWjM!7ms60{;vl~e=i>jwP!^>U#FW0#LQccZ!7EWyOT4|by?hfbo=&jOlD(m$voZRTA!Mz%-K;KmPtJc;U3K5hDHr4e+w~x znpml9%NbO;-G=;jAsfCu^YL}YIi^IpZB%~DubjjMcKA4vJ<<{AyRdqP^W%EtEn7Mx zuYG)>%x<&#A>MS4WvH>7L7A~a6j5M_z74S|(CkAdTqxzvXr(U}P*;Cgn=21{`&7MD zD}fXz%t?%wpGUo0eyeqxjyB}^NO#o`)5W?*+`4YiTp4=)s&xKiX+Vit#c!M#^z(tK zXW;#DnK+&}dwB{+>y2M)FC;RX^NeDuukO=Z|Hdh`8g&57e!_hC%|B-|-O9x`mAYK! zI4wod^0e^hP2j=0q4m~ddK$W;#sGt?j3{H^#(?#3oclWau1#-AR5?vJn8!mJzrJ7v z&ya6PMK^CYG(l1VN*xA;xMy}!<_(JOcbQbLv^^zOHW_MP5kj$u1{c|*ZE(zG*Y989 zH8>Adpp5Nykrb)25I3g6XS@UVPmL7Vm5*X_m(VI@ANc!bTed!P&T%$u^%ZV`j} z_T^SOj}xKf_}B99JcRGV@-WkK21}2qT9@S9RMyCtCu+C8z6JZzgswZE{GLq>%;%kN zpo)JWqOG8j5pVC@rGg29gWaGm`H7_DB8NZYaSD*&k}>by;3i{{9YCB@Hd%f`0U4{^ zT4PQ_FT}0!m}iBBk0h3Qhb+H2gd7#df(D(n>v%BOjP_L%?qI$26V=|KKH{h9aRj3$ znf|Eze?q10D=eGpf&EEeI6YguwAXX;WbO}ST4dG?L?$pDbZEO8h%K0CPpzwhlP}%3 zt(Y!ihs@UD0;eyzjkJ7P=*_CoY$l~;>=!{veCn(6f#$GyWpm23i3Yy9Q1O*nBT;OC z^Zx;AVMmEYpy`e}yhlFp3}0~ltCYM60lmyt(XtbNOePzR^_KkHx>%+|B}KaqNV{|F zhquFbdwzwL*X2kPon4i|)84UIh~ZWQRq3ole6e*)FaJsAJ>Bij1;W)jU%N;0R$Y&)JmuC0;FM{_4aVh6z|o?9qktSur+kdt^(%ze zzl|BeE}B=vqLCpv*15K%?D?EH`ds}L1e=sy@^fmNGt^L1Dx|o-@TdX!lrQ5K%+}VE zardBKBir13<%A9teOD1YA_E&Fs_l}L%=H=#~x zpfuA|quigIJJ~ihErX+h6`%+wZa;1RVaD6TBl~=F`P>qSuZ|jTJBs9fxbVk4>Ue!| z;AE*hc7fIjk%M~Gyr>03jrZ@Yn@04KI5ZAoet)y4l z=&(l)uH}EqM!oY#jQI*}lcI7u5hAM9g9kbV`(ffWZtN`Z#mx4ZD3AMu3Jt%CYk@0O z#L#@5-cfWc5c`p1Vr?^(%cC^rbph^)+)-NvJ#3yjE;hR}mm(okZZnS0oYLyh3AS*a zkGFm}P>IBp4S58^jcX!yj5<>3no<@NAI0QGlfXt?;%7>ple&CkG}R|rLnV122r|FM zZmp1#A509`G)Og73*X2OoUyj}x&=%hpMouIWM{h4mCbOlmeYTzjf;8vc(-nTX^&02 z@hBDP)Z5MwY@8l6Wa*tV{rT^Hn2FWCp6D;C)0m&5Nto6Fx*_8@pMzadOOsyRGY}HR z9<6r9w!svA8*keAsi7XIbPTW!7u4sI?EaN_6rC-iJo3HYdhCMVxD=S3W^wq3-sllJ z>5VIOGNhwpmeOx|$Q!vnIW%jL%E1iCOOhe*7a_u1bks;lf#mc2a4*rc>yDUQE{drx zD$Fs(9uz&>+L&RO9|rNLmx>nLq?)fh^Z;xh;u7Y`*@gI5hmONNQ1e5fK7$+XQ45$$ zo@g>emRWx@6e>rPxVJdFLtB$s<+vo46z2HgyDk9GFmlERpOzl`fMJ^a>m${nMe$2( z_kFvB>NuOk-!2RO7Gt3L-QB|kGmH2sCdI9(=DNF>^(Ns{c*HQkaPFZb$Am^F&8p?l z1#LaLEH)1=hNbrn;R zW9uqJ_4su0nQFrZ8Vcu?_67_FoKe5?US&uxh2YFk31*lt_&AS2_wcQvd?DZN z;?`MAOW$*+nKqQ$K^s`9++2BovNr>g`5zTES53Fom()VUu!;24xua*y9Yy0i< z_D45v!plNhFKvcuz__v=2c7n9e7E8w>I1*5)3dT2vmWOQME{uVm|FMP;USUudmBHc z#pGX6s{G3E_$oZCjeN|&fMwU8O8_cE7|Ek3mQYqmd`_o2>l~G+Fa!OUlK!%{B z0QTsc-pk~k+xX_^>|r2i3wUSZ-BQN({04|Vc10!;WL>)+6(4Lx0$vqBh(X7vJX{d5 zDs(02&K~lFU&f>)-{iT%6?F$aY|w+mao}z3^dQyCORS{0X#B|Up!1;Jn39wx9;Geo z%go(HzZi{bK{jzHUnQLZkzmnl`jkD?U`cKYTY@scFvB;!I$0v zHivQ@3E!}-0o;6?hfB^4GP2KKtvRs2bBlv|rbPW7MYd!gj6!)j-TzgT&0Gc4=>4DN)jwmq54f%Olo4nQgdm z#UkG~fxA_NPMc>W$--^0heZja9i&Ux=KEO-bGk|@*x%4b#_zN`%H|Vt)=pC=FZ@Rj z%hSr0OH<`65pB`x+XCxw$_HRLDgV`3lj%(VBNABFP5uAe1>G>cqyN%1sqpoLlGi$P zK8#xi`nQQ^7Lx+Uy<&b~&Y=F3+_CJ`1QAm!RhLn>m<&lHN0hdmR_IP^mHXZqYaHyYwu1;O|oINQ+hJ-}>slqM0jp*0YR!^9dvF;~tZWUv~H-$ZM)+Rajx6-^q3UyS6j=l}2+N$B4^ z7UA7)9C^8&vCd?5YrrBAnKBVQN+WQknhSs*u9Ejwks6PRuu!u<2F}*XZMt3 z6b3e1t)17se!4!pV`aUffn=Pw!^4Jr6YkUH|CS}B5M{c`PTw-PQvZVdzp3>6NBjOm d?Cv!29QD0O8gI8}&re6tQrA^0SAF*Ke*pc5sxbfn literal 0 Hc-jL100001 diff --git a/docs_src/extending_openapi/tutorial003.py b/docs_src/extending_openapi/tutorial003.py new file mode 100644 index 0000000000..6c24ce7583 --- /dev/null +++ b/docs_src/extending_openapi/tutorial003.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI(swagger_ui_parameters={"syntaxHighlight": False}) + + +@app.get("/users/{username}") +async def read_user(username: str): + return {"message": f"Hello {username}"} diff --git a/docs_src/extending_openapi/tutorial004.py b/docs_src/extending_openapi/tutorial004.py new file mode 100644 index 0000000000..cc569ce450 --- /dev/null +++ b/docs_src/extending_openapi/tutorial004.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI(swagger_ui_parameters={"syntaxHighlight.theme": "obsidian"}) + + +@app.get("/users/{username}") +async def read_user(username: str): + return {"message": f"Hello {username}"} diff --git a/docs_src/extending_openapi/tutorial005.py b/docs_src/extending_openapi/tutorial005.py new file mode 100644 index 0000000000..b4449f5c6a --- /dev/null +++ b/docs_src/extending_openapi/tutorial005.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI + +app = FastAPI(swagger_ui_parameters={"deepLinking": False}) + + +@app.get("/users/{username}") +async def read_user(username: str): + return {"message": f"Hello {username}"} diff --git a/fastapi/applications.py b/fastapi/applications.py index 0c25026e2b..d71d4190ab 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -65,6 +65,7 @@ class FastAPI(Starlette): callbacks: Optional[List[BaseRoute]] = None, deprecated: Optional[bool] = None, include_in_schema: bool = True, + swagger_ui_parameters: Optional[Dict[str, Any]] = None, **extra: Any, ) -> None: self._debug: bool = debug @@ -120,6 +121,7 @@ class FastAPI(Starlette): self.redoc_url = redoc_url self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url self.swagger_ui_init_oauth = swagger_ui_init_oauth + self.swagger_ui_parameters = swagger_ui_parameters self.extra = extra self.dependency_overrides: Dict[Callable[..., Any], Callable[..., Any]] = {} @@ -174,6 +176,7 @@ class FastAPI(Starlette): title=self.title + " - Swagger UI", oauth2_redirect_url=oauth2_redirect_url, init_oauth=self.swagger_ui_init_oauth, + swagger_ui_parameters=self.swagger_ui_parameters, ) self.add_route(self.docs_url, swagger_ui_html, include_in_schema=False) diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py index fd22e4e8c1..1be90d188f 100644 --- a/fastapi/openapi/docs.py +++ b/fastapi/openapi/docs.py @@ -4,6 +4,14 @@ from typing import Any, Dict, Optional from fastapi.encoders import jsonable_encoder from starlette.responses import HTMLResponse +swagger_ui_default_parameters = { + "dom_id": "#swagger-ui", + "layout": "BaseLayout", + "deepLinking": True, + "showExtensions": True, + "showCommonExtensions": True, +} + def get_swagger_ui_html( *, @@ -14,7 +22,11 @@ def get_swagger_ui_html( swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png", oauth2_redirect_url: Optional[str] = None, init_oauth: Optional[Dict[str, Any]] = None, + swagger_ui_parameters: Optional[Dict[str, Any]] = None, ) -> HTMLResponse: + current_swagger_ui_parameters = swagger_ui_default_parameters.copy() + if swagger_ui_parameters: + current_swagger_ui_parameters.update(swagger_ui_parameters) html = f""" @@ -34,19 +46,17 @@ def get_swagger_ui_html( url: '{openapi_url}', """ + for key, value in current_swagger_ui_parameters.items(): + html += f"{json.dumps(key)}: {json.dumps(jsonable_encoder(value))},\n" + if oauth2_redirect_url: html += f"oauth2RedirectUrl: window.location.origin + '{oauth2_redirect_url}'," html += """ - dom_id: '#swagger-ui', - presets: [ + presets: [ SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset ], - layout: "BaseLayout", - deepLinking: true, - showExtensions: true, - showCommonExtensions: true })""" if init_oauth: diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial003.py b/tests/test_tutorial/test_extending_openapi/test_tutorial003.py new file mode 100644 index 0000000000..0184dd9f83 --- /dev/null +++ b/tests/test_tutorial/test_extending_openapi/test_tutorial003.py @@ -0,0 +1,41 @@ +from fastapi.testclient import TestClient + +from docs_src.extending_openapi.tutorial003 import app + +client = TestClient(app) + + +def test_swagger_ui(): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert ( + '"syntaxHighlight": false' in response.text + ), "syntaxHighlight should be included and converted to JSON" + assert ( + '"dom_id": "#swagger-ui"' in response.text + ), "default configs should be preserved" + assert "presets: [" in response.text, "default configs should be preserved" + assert ( + "SwaggerUIBundle.presets.apis," in response.text + ), "default configs should be preserved" + assert ( + "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text + ), "default configs should be preserved" + assert ( + '"layout": "BaseLayout",' in response.text + ), "default configs should be preserved" + assert ( + '"deepLinking": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showExtensions": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showCommonExtensions": true,' in response.text + ), "default configs should be preserved" + + +def test_get_users(): + response = client.get("/users/foo") + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello foo"} diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial004.py b/tests/test_tutorial/test_extending_openapi/test_tutorial004.py new file mode 100644 index 0000000000..4f7615126a --- /dev/null +++ b/tests/test_tutorial/test_extending_openapi/test_tutorial004.py @@ -0,0 +1,44 @@ +from fastapi.testclient import TestClient + +from docs_src.extending_openapi.tutorial004 import app + +client = TestClient(app) + + +def test_swagger_ui(): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert ( + '"syntaxHighlight": false' not in response.text + ), "not used parameters should not be included" + assert ( + '"syntaxHighlight.theme": "obsidian"' in response.text + ), "parameters with middle dots should be included in a JSON compatible way" + assert ( + '"dom_id": "#swagger-ui"' in response.text + ), "default configs should be preserved" + assert "presets: [" in response.text, "default configs should be preserved" + assert ( + "SwaggerUIBundle.presets.apis," in response.text + ), "default configs should be preserved" + assert ( + "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text + ), "default configs should be preserved" + assert ( + '"layout": "BaseLayout",' in response.text + ), "default configs should be preserved" + assert ( + '"deepLinking": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showExtensions": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showCommonExtensions": true,' in response.text + ), "default configs should be preserved" + + +def test_get_users(): + response = client.get("/users/foo") + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello foo"} diff --git a/tests/test_tutorial/test_extending_openapi/test_tutorial005.py b/tests/test_tutorial/test_extending_openapi/test_tutorial005.py new file mode 100644 index 0000000000..24aeb93db3 --- /dev/null +++ b/tests/test_tutorial/test_extending_openapi/test_tutorial005.py @@ -0,0 +1,44 @@ +from fastapi.testclient import TestClient + +from docs_src.extending_openapi.tutorial005 import app + +client = TestClient(app) + + +def test_swagger_ui(): + response = client.get("/docs") + assert response.status_code == 200, response.text + assert ( + '"deepLinking": false,' in response.text + ), "overridden configs should be preserved" + assert ( + '"deepLinking": true' not in response.text + ), "overridden configs should not include the old value" + assert ( + '"syntaxHighlight": false' not in response.text + ), "not used parameters should not be included" + assert ( + '"dom_id": "#swagger-ui"' in response.text + ), "default configs should be preserved" + assert "presets: [" in response.text, "default configs should be preserved" + assert ( + "SwaggerUIBundle.presets.apis," in response.text + ), "default configs should be preserved" + assert ( + "SwaggerUIBundle.SwaggerUIStandalonePreset" in response.text + ), "default configs should be preserved" + assert ( + '"layout": "BaseLayout",' in response.text + ), "default configs should be preserved" + assert ( + '"showExtensions": true,' in response.text + ), "default configs should be preserved" + assert ( + '"showCommonExtensions": true,' in response.text + ), "default configs should be preserved" + + +def test_get_users(): + response = client.get("/users/foo") + assert response.status_code == 200, response.text + assert response.json() == {"message": "Hello foo"} -- 2.47.3