From 6c02a5687785f8ecc25c185423584402c5e4e920 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 3 Jun 2024 11:36:47 +0800 Subject: [PATCH 01/16] chore:add Architecture image --- README.md | 3 ++- README_ZH.md | 3 +++ docs/begonia.png | Bin 0 -> 283557 bytes docs/overview.md | 0 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 docs/begonia.png create mode 100644 docs/overview.md diff --git a/README.md b/README.md index b31e858..747839f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ Begonia is an HTTP to gRPC reverse proxy server, which registers services define - Allows requests with `application/x-www-form-urlencoded` and `multipart/form-data` parameter formats. - Rich built-in middleware, such as APIKEY verification, AKSK verification, and `go-playground/validator` for parameter validation middleware.parameter verification middleware - Dynamic registration, updating, and deletion of gRPC service routes based on the protoc descriptor_set_out. - +# Architecture +![Architecture](docs/begonia.png) # Getting Started ### Installation diff --git a/README_ZH.md b/README_ZH.md index fc84a08..258221b 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -29,6 +29,9 @@ Begonia 是一个 HTTP 到 gRPC 的反向代理服务器,它基于 protoc 生 - 丰富的内置中间件,例如 APIKEY 校验、AKSK 校验,`go-playground/validator`参数校验中间件 - 基于 protoc `descriptor_set_out` 实现gRPC服务路由的动态注册、更新和删除 +# 架构 +![架构](docs/begonia.png) + # 开始 ### 安装 diff --git a/docs/begonia.png b/docs/begonia.png new file mode 100644 index 0000000000000000000000000000000000000000..551663395d682f30d56a913a4fbdc1a8226045c7 GIT binary patch literal 283557 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 I4*D}d2CL%fihEAy;tq1Qth)(gY@W1C5es# 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⁡a`vPIg-J(3FVPV;4+%5jF(v}N5XqBp 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%0AWM+n{BL_OchkZpEcT2_ZxE9o>uncv0+r~ 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 J?(3g8^Iy~0vZDY1 literal 0 HcmV?d00001 diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..e69de29 From e5fe9d5cf02ef3f06bece35e4bf13c9e1db1a696 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 3 Jun 2024 11:51:18 +0800 Subject: [PATCH 02/16] chore:add Architecture image --- docs/begonia.png | Bin 283557 -> 272413 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/begonia.png b/docs/begonia.png index 551663395d682f30d56a913a4fbdc1a8226045c7..1db672eb515739830c73aa0f7b53aee58e6f26e2 100644 GIT binary patch literal 272413 zcmeFZXIPWl)-G%ZMMQ)MB28Edf{3Vy(gi7kH0d2ddJ`$3M^r><5tX7e=|P(G5+Eui z2qIDggn-f^fe;`GE#ype@AdBY-D{nF&iC(JpTEQyo@b6R?=i+b?lGs&bhXu(4{;sZ zwQCpiP4yeMckMa|*|m$oo$&zpWGPxi0{pSt`?i|WuHvuUbGvr&@49*8x`DqH0S8U7 z_YG0lrhcI`c3i2_%Rb*Aa{I>a>xUoi*UM@hl&mrdCFQm5V{=`u49+@p48pcw?*eO_ zy7+;kkN0PP{#54UXKGRPzTqnodSH6=n!exk*O6=9)86Sbr0?+8L5nlN@?F%;loOQ3 z>6F~}to(`(cJ1D?kMZdL_J_BrifqjP@ms&1RKamnq=)4{J@MCfC-*&$yu9x}zklc3 z*LG(JFkk;aS?vQwZAFB-^8enXe?I6r8(8kq-^=~&T@VAm!SFL3{0_C9eDE6#zrpZl zb^Hy6-(dI+hCe;<8w`KC;&*2F4Tj%f_|pTw!SJUmerJZ?VE7G&KRxgp41c=fcV_qv zhW~$n;RQ1!wYThh3@a(}>7bNAuIi04*Z$spT)J;h3#7$74QKVjZ6?RoAa*mKd;On; z=)*~h_pc|zTx)5*O_@{aW&#xM3zB=;_;p3Xa-T+2sq~Hozn@#OPH?e3qx?^K_fIeX zj~|{O6mMr_!aQpyI>kz94^Npf^`23Do4!Y@dn?3>4GEGY@joR*3>H4ab~)j6Qgp>J=%T$jcihv^jov;O9zOBh8JifQb!_{<| zGX0cTeIt6lch~5Rzga$GpZWLF1gDJ-!DI(ZYhqZ8+Rrb}p8}6YyE%leLRiAnnKyQ8KO&?${Sl#AT+e<&wNH)zHOpw1 zvnjuUL3-MSLt&@H`3+u)|8N)6^l**5YTL#!u;)+1|N9S$Z}!aabzk0ecRt_wZs(tD zgqav2%v1BPfu}7GF+vPYJzN9klX9Kv^a}oVkTC@rJdVw4$aY>VpGwVmy)(>F6a>F4 zY1?1)T5)`jl1l#`ai@U!UREt@t@_D?U%vS7E7`H+Nrj?*MM|iDZ;Ca`oJx}HjwAjM z0=8J(6nnaBcSfcQ%Q^6vO;#;pH{|bvLNQ>!nSgxg-ClxcJoJf!>W<%Ig!v8no*Gs= z?0E*vGi0iNYyUZ%mDJj-=@IZZ&#_Tws<_cHq;{oq{nK-+$Di4DjJgj@`5fQ(kn!kI zk$0324-;}YR4W)E*TnwamY<&}KHL8$bB@rI>~IhgU!$4}789n*epJMz`JMTtu;%@b zBj4>mE}~ICBnJQMr1Jv2NFy`)IG8tu?F^}2i01ES6!saq%_5N0+Rna#<#<-cAq|3UEOKiZvG!7r+V;gxXh3p0=b!2aHq?L8(vP#8eDXIDSqU67#g~tv8X#~NUtF%)ZAz^prS>^NWuSeA#<5%PdCw|{i zt~V&DupO6UXX7*Dwo3~*qgrUn);<0L@208z*1q}a;N^h(h3PUhT?S4<6xor< zpm+y7-f({VaiqgGk-4>W#HG1Qq){| zZ=w)nR5;{FG^riiG8=WV{x>_rxPY?fvakH7TFov1{KZ)Q2-uqq>@`G|WMr1=$e%!N zFB_{Qbe1x{(y-~6yh-|o9lK)V&DH2(`K59E9VfLmP8PQLn}c(5?N(p%PV`s4u@|B0P#|8I!qG6TNL=rL#%cspuI)J$U;V3xe&o#dPa>$;2(mT?w)&{Rgot(o+a1=%0 z+c&TV+Sfs8KQ|n+AAP9HZZ$mt6JoLVn6Q$~#Hur?EqTnKIU~?Fnh`;M^=Ua(Uw9EG7w0zPa%D{Q;?Cx`)Y@YzvSiM#MZAU3Xn-EKGKC>KzC z%eRAL*^JE4%P*?0)PGQX9Qnk3@MsYI`^b~({TC|s^9FYzHM}C8r!Ll9au{7jX2dvC zfD6rS7CewGFY_9t&EC9NpNR=g=KL4mhh?1QH@JKm?=S#7uWrfz8s;kll8EMfOPTzh zeSttAH}E|sGH=;67^P^uXD_P=SGk)h!S9Fo12)c{J@7PN2e+ha-ny6zce<4&sLP@U ze2*N_exMkzFAeKR^q-$JPH*zS(dgS)wBm-3Af08*GhX#a6}Nk3FOEHg3P!6G?Pc>4fb>g>z}Za`x@p zhsSCpRKq%k3R<}f(d&GRE z(n#wMieo@rNgjGN9XJpbzyu-h`F#FoI;;UP^<(mg`hyROJ5UY}k33L^jZQBEAGh~c zR&6sL_Cq)g^FUiylS47-r~Sou?>TtLZPYo>WNV=Mm4p4t`P$2^3KSFPX*+>^jD2U` zkH6h(H$)OISkUqv4O;#bE`fjNIxqX~2V>v)3rRCgPb=}ZUrWSe2*t1~Acw6Q+gvDd zY)D1w>Uri6e$HrAK%_zP7fkjs{>)r&{*xf-n%fL{p<5%BkMO=yiQevA9;7)c@V&B=5e_Tf zcK{zeI5lqFQX`aRzz!bKr)BfL*s;M982x{>iWJe3*k>%V--x~-bi!8m9S@y-)ZH^8 zi(q2g$7vACAf#&?R+OcN&ZIUXi|r_W?}v;nPQ2u`=)>X`)Bwg0Oavz_eH%{-8k*YM zc!dEVr*z+ei9n;M_quvwW|Y%AbtDtik;oT*+zay{Sg0((Wsm!ip zJRFFaeOF*Nu9T5k$KK1o?--k~GQw$t=o#Z&J(`eIICa#?B20)*=r5gCL2?}WR^c8* z*z_EWa&kVqT60A8tx#BISHv}AD+n8(|NA`WfYcqJFtLZo71%#iQmJ$rO5DlFk;jgT zh-eIZfq=XP0#ZZFz`z81P4^){`oZJizI2U&9v1f#IA}28t+ofm*8{a2? zDB4`um%tZrV`~nP$R0Rbs+k^Z2_spD^Y(sZTvdHLyK)Ck-?|wcyjVMWt5Y7p5^?kG zEZ$V?CEMSYZI_J9Ps3lsj)A$B#(PA90S7=w-36}4yTW!K-7z1&BK|Z^)adYs`NKCK ze6V3G9EOgWY_zj6$*nf4u(YIflnAdT0pq3isHC%OhR#@Leda+1>T_*^ikYR@jVU+F z6*4f0N(lKG&%)s8nb|xMyTU$!-FVlhq!M14y7x2~@V&Cp6U#pjoZx%sGUM(&d--)E zb>omChZXIaZ`v&F8z9s=$_C7z4cE5mp;6IR4uHRt*EeL?teE_ZDz#|Ds;JLQ@D^u#gi~3`F6v5g|!AicqeIlsH|LX-YKVQI* zky+*!7Qm{m{I(5mT4W2*5;>Iq6D?qbD55kZ0gNyJzuFGa;)K6uVhbGg4AOUheuvIU zY3ya?*L4JzNk8I>-34Zga*E)tT^!#hCWx~fFy;2or{clN48LsuOjeJ<%2?ZDuRl=C z08cXX$jDSO@A`NQ#DzxT+XQ{79T4boJ>3Pxv|3_!yV;qqsl57r>4xipQ-YckwL)}*^}yC`WJ^-& znKPx7j4DPj7j6NC){Oeb^EGf|y8*NJ*_U8>uixDPiS?iN0Q<~sF39_=u9XKGcl(o% z2`>i&YmED&!JZlaT*nTj`S07Y*d#F^Zp|ZlTs*(td!*u~ggtynhTC~=fu*&o2rIJr zMLwA5#4CR;&2sgwn@MRdOS%8HSXL|i*>qgfrd;bP4I1U1T;19u!?AGhyjG`{#Ql+3 zPATs^PMONaj1>3es^(s(c&s7(Tue*>MukFt>XjwP#MpV&hMIK2)uq}yi0}kCv5eZ* zo+3XQrL_Ccp~pN$U*rDlA!zsLR2)?Nl@5MkK!kyea5B9N=O=LcZFF@$m2D*Yj zQo#n9?(h?{6DtB2d_bB5pv=s`isyILx2;@92$MYkiFhQcJ4r4ah(84B7jc071iTW! z3geVK_e7XA{d}>)rh*7@WMWcqBD8X=e(L{PKsc%l^_GV0YSWxF~_@>&;aJPD^! zisiqIuUFqnN4Z9)O4N?o)o<5#wY)jqF_BlY?p88$@y^$Z!5K+4^hZyHYfs?3LwKt1 zhy?P{Hy(v8Em@x-q8&D&2p4;4cc;@$$^o9csmDqr#(%5Ehqn9*w@b z5S0FuBttatKFHg+L8)0J!r0BFJQm48EP?o&q#m7=cq}SUSC6vsVn2()XgSdp-YLgh zgkj+O&Qr4eg=$BNudd{s3aJhifAd6=Wr;P4&kBKX0W>O@e$7@IM9*AbkH)+x*gtEX zED(nZAa*uP!QqaunP2-Qfcrm+@mdx%!Vxk1q$Gt;XLK2@T|QXh5aMRmP`pfwn?P5L z4k0G0degjZ2^BYKyc4fV=!SVR5PP_E@`WI?#pmV&o{`L=Y52=<=glus1P?W>&IXo` zvR=InUPI_c9x<`?FQ$H9@`E~`-4Rr4ev{MHx*QHiQ~cnu9Y;c&8MNU|V?k#2ymDYW zbf-U!y}*5*M+=3N9eaOFM5KOSIG-*8aqm9EEzp+cIOLw_RWLbZJ>5b z8MG`S7dZK(4?&z8%MP*cY*c(D)0ZekuAkuv0Tt-KC!dh3s&dLIIG*jVe3r+nLuV4> z6Ip^go_Ve2jT-d2x$ zIw?H&{8d_c>0B*2o>qRLo0JX<`Vv6e8sT(JEQ-c%lgIs+dXh!QJVBt3`u`Y`2*A89 z8aw$^SFeMzP#V%v#d*c$M#(@$R_c`!ET>>=?C_6?FT8bI12xm$3W&pk27R8C9u{EY z#pMu`ov(Im*d6NT$w)z)N4TvIKC02V>(pZ#K$nV~ODnG07 zGyCq;g@1EERz&1*Tk%FRv84$!qQ^QyFEnm0O%K&9xgm2PO;wAF!_jI9DiJ#U%3}=H zt6+JQy1iA`j*mbQCB2$At$R0obuu!8&exndPU{kjr#kE|N@W=;?=3Y_Al(`X88sv| zcT!u1b3_H0j>mAyNc+kA!v~39CXk1;MElAH`MbVX*bRLK)qg%`P>C*2hn&Y37 z>6`#eZVf9vDT35zf(om3ZHt7yD%=JxYT`4&zr$#HagSukc5xB%*08Tb%32O}wcS^k zFiCRk7dZ%F4v&>YmjU7#UwLs_au9^aBg@@rA;q^cAe}XUbk^{XB=&!m#~(+gsUeA( z_=(n%?FxY;T#hi(T+g#A>Z>$T(xDhjRUVr79=g@8 zW9ZjG8JdZ<8;8lS77dnp*yWvO=99aqP2bM5-3UciHu{kr8~LJ2>Wi>BI=CRKlz@*j zK~2(sP*_FYCENshPDSC(gq@hS!0*zb&+FT&z3Ol_s`zZjy$M^YSPZ_Mr4&!F5d8A+ypCZ3omB@=| zuc4u2;W{sJ-5Tjx`qPAjgn^LcH^~z`m{37gt#6hX$$y>&;KWV@A+Z^#j7-AU8;K#4 z)*{|qask2k)Mu^Qshm1ni;V-{Ze#uWtkX|0plFlr7}I;RBZ(mu%f82p&DPs`OlX5; z1K)dF`(5pN=Y4DQb;e-T{xrA(cB7$622vEbIyYE$N8ernDl+dabNIE?Aqey9+pnDt z?u3mAyS_*`$m(a?ZH|FMf54t>aPI2=TiBp=yPIo!?G`nsiTAQM#TzBes@zKmE3vMP ztfu#JkVO*{AIs3r_He>G`vTu+NzDYbj`d3Lx6{}^^GQ&frovK;|9n+=JO;k@Va{tU zNC)o~*|BKF&4F5Rji;Rkao@Lz<9*3zYA|&&uF4%LNNu zu@o$9%j;bITI+y#IXa_ZVHp+~H=$Y`nkqL;7@2sNk(oQ4%XuyIR?3DrU(9QUMucyA z?CwHB5NsPSj($AUD}r8|l4(5FHL|Hbg{R^+8l1QWM#K{@HV&mmzB6PM5wVMS)2j<~ zH!hL*i(Q$(m66AxfS(3}iqmI)b`#`X^RSO=-}DfO>i$ye?q8`##TY|kyqusI2l%&r z_`k!bTf(3(;*_m$MC1-dVrqom9HDod6w&l_afy_oE%Vv+)Sr;MFB>``w^KeWhdc*< z1fl2XRw2PA@V!2z%0NHVBwDa1*3YDiW-a>8YmPq_ZNfK z50*l3_?%rj%cO5*(O8+}_FA4{d}3=fC-0_C`p5=3Rj1^sJGS7*7qbEd@2w=0#!!6} zI!VgQ!fmulZqcgBHbURY!zHp*@CiEFE6XlVA}df1w;(3wg(-`V4~XTGmpzg~s=qE* zmE$VatlwowOsm>_bx@-6$$G!t5i+MT0>K;Es;&nrUCTQ&wy?{9l=dL9437%G{6~t< zJ#rzD^2^VG@LgeH>^sqlm;Q@r?G^^1R(jViPpkc)JMR72D@CSbH{DTE_2*(Y3E04# z^(9GYvmtDyp}ld%st#7aA;X972O*lTkdYY+^=V(M^7(GAGw*{~p%dsa1U<*0D|E7W zRAR{DI@#|qM3RN?II->rR>Ey^X@paXw$e2|nr>;nZbd=V5aPVtlXdk_W;W`LwX8}i z)^$G?Yl6KbZ&5#IQQ(+Gq-HhQtR~pEp4u~JXw|hxTtR+faG=DR+*@Fan=T3|CMQ$I z#Lfe5j6$z1#jOZ3O{b8f@Ii8kLqoU@O3F!3c^fLHT(C(su|vtkx?wJ@ro{5S%M8hG zJoA$EDBjMudV-ShG#$5jZ)=Gq%1l|$9qR~}6V}FgYopNOfxAE>*uvw90LhR!UgNb; zSSx5gT`N*`Dofkh4hKBnuh%G#P(|D{PNfOY*=5?f~46!L}DQ0F2qDZ$&AfCOcAFkt-u( z%|&Ak;B0haIxVrZ%Qdz*81!cP2VV@DA`spu^qC=*MLvtorKBQ#t7Dg}{ex|>)M^jU zXDcAZC^QCnxy{{^$_)e}mKn(+n4B9l5$0$NRpJnN_aZuI>)GIxapE6dA*D}S0dH^_ z88*KbRL{)-d;mv0ul zmgBs7j7O18abM*qf~EFNx$W}Gat5z(ElxAa)M(o1ydz|FiVw3tm&P(G7?Lj*n z1Qy^fdDo!VMw~E`uG?J++iWrcOy59J3D>Q`k-h;>+;LGX5WuZQT=Slh9$~n7h9GwI_p86LRJTves;9H{knAi>R(5g%jW{Dh7cXh}ZX#E*Da{4(MrVJkE{**nYK zqv=hu-B#NV3CB-^18PSo4(4Tk$|JR_L;eo9*1>vr^IP1K7p*Sa2XmLL^l?fS)=DYA z@&n-ONoZ|myj%|oT2u9|U3+{venEELN_FqR&9^(f8A4m-(&3$B*z5sv1(o~$XG;(0 zzSAq+Zp1UYi9Xo6lQyEZ_L{0T_sNM|o!B2)YLx5Znf9BMHMnuG?3EInZUFr?N3eYE zqFf1@blf_?oKS%oNh;ec9BHJ1y930;uTh)yO_=C}J^V^?NVB|5N=F;WeTjA~Za6gr z0$n!c=MU>;{cM4CF%Ot&2#=IbCI}n!rBa1T>cl)kTmq)1#MAGYa94MHdDE_s=j7po zU9n$ntC{Q+@{@L;)KoDo2K}+UQXFx=q%wU%%5&Xri~0#|W}PnWU=X}|db1_bqo!c_ zX&KlDBhO*(Jj;7pQ-eYa@}jx1JUj|0Vq7~F$UQkH+A}f()!OfF+;+2;AuX*% zoLqd@k##^kXf}UsIR;8~kv7%!Ob0u~yaJ8Ur_64|g?3;(qf5q?(KU0=?7I!WdTh0^ zk`ft+Cma{5&Fi0U;`>WGDqjAzV!$r)4ho4e76&R05Lp5ZJFf!rar$S!3`pvK^~?V_ zga3`Zz#6H`{lMuw}E)YIRT;JqPH-{pS&ikz4aO5 z%M9t{RX8K>ySm$JlHgnf6_cKcWf1iSjFXa~+g(}&{mKO>2gUf6-E`+uSa3^*L}qOu zCWG+x4&PdOvLI4ZPY0V!9g~yynQW9>FlxtyM{1ZJe+jNh2t1Cg*u8rX`5(D=-cn_v zU-K7`tqd<60YET?aWk2RAabRjB^4Yis!xMC>2cz;RD zbpV~ix2Af|dd+`|3=A7_92?m11q?!G#K&`MI*We$1Qc$}%PJH?4oRIZb?+G99B8pK zJRoAQnwE?@*`yjftKj!p74%QXZ-CV5oy#S%G4%;Oak6^E*z}&CP!7REWhh~PBqimd z#E~S4+T}nnly$x0{%nO9a&%sEKf#eS`6X(gAd<#s$48G z>zKOfj*P`FqEVamgfbi{%LkM%zmg<5kllB@+pmN`7)fmYysf|rJgp*3{lN()Ad2{L zG2`HIBx%-@_%8GTW`uji8fHO&PLm{jOKOY8#z1%CY9ow%`RyG#!I09)+vzu;V*RwV z_e^DI3E&by$7&A93zfc7Y=)IKI?f%~v_HK|&UPUmd%Wt&8&xgNlS4cyv1{K?#D&An zW@1St#b9iQETcrNsg$^T z+2{1_TvKy;)zhnPVh$tZ=4;%T2OYAOE=645 zZ2aO^Pw3K#BzWDabgPtid2ff9ZKl&nd5(NA^*m+yN{5i(8BnQPh0^ltuN3B=4JA*r z+4dmfO8r(6u1?Hgj23q#;6Bc7mRysjBwft#r4#g=hIm>+H%-W~wnTZKk^!|0=1MJu z6Tgdj7tthJc zE|jlsO&rBlFf4Zr;a)xcqsew{Hm2oJp&7aC+BK5>kDjqz42z&T@P=0Hm*fU*nU9_l;9yEbsObl@D}3{ zqt{KZI67lPx~`t7avNliKv63;-X_|x1J#t3OTxfs>C4$t?Bt9>GttGo(#v~MnTE@| z^^`BYbmFaY(W&s_SaUq5CdT7wd8k(iIhaT8;`xA;XSia#o$&}4^jt{Qa-hzg{Q#!o zM*kePcF7F9kI?P@#tgBy9|a?ht#ugvN}6eP>1{I1fBh6G66eseL%~svK+82I428G;ai6^Gk&7`#PvRv)E0^~}g(#tc+avn>i{dluS!4qoC@LIOk z)@HqRWHi}#YG{xuUNUOx_O-dR6rY^|H7;!|h6$rU4iz@;8aen%|5wC_IEp73-q>l9 z<6;FBZTNr5e@Fgqi;}DFe#~^GQzA%fG1OI;ZxS+E(QK%-#iZUvuQNj@H&2SVI6FVX zcWuLW^daa~_&GP9c~tNpxx{L7dsJX}{IxVV-iQ@W`LCY4>O0tT<7HhP$$=&7gzIAvo} zZxiiG#pRf{HI6_Ezd%J?%xA|eVU8A<^Mza899?j!s?FC8ckopv)d8B(+*b<8rJ%&Q z@|tov(yO>c0vF_w%|h07u&z#%GC3f?nLj5;Z@0Be${mp|ti9F>=jQc36CFd42*J6V zwoNbEiw1I+AbF0YONy1H3tGCvORO*(oG5$zl`2Yrx4u5!!U077N}g7v=>*y*(ERwP zpkVR}6i>kW4Hi0gZ+%J`@Eqc@uANI8cyP~;;eFMDVbGUnT|CC=&qs-A{%7Pvw8#=M zp2M(e&>-}y@lfn}%&V)L`O=D?z{(|BW4L)&@(lU3hH*9UVzZMS7~`gB%LS?XYwwe; z-oNoB_3HU2)pvYLC*-;cBel${jjXQOw7zQN?N4eyW58bOKlP^Vp_e<><0TqhQJRH3 zN1}BYx72m^hjL2Yzk#nE>8Gp?Gm|EnVEbM+q{Y0B!6k1Kicw{*YJOU-SZRtQnkp$N z*|Nk@96ls?8{IdE45@vxFgw!rsg){UUe^D;R?FNLPgy8Z*o15j{2F+jL$Iz$6Sgm8t|4)raoHW?Rpb~?8x?#up3#y ztfaY=Wl1i``P#Y;mfPu21~S}HJY@SKJF-0;wCAOI$|_`Owd(4M=1-2s=q6E2YdKy@ zm9iN?Dfjx+#wy`CUUT(c>#L-1Kd7r!qnvGHSsOht`yidT8RgeXDnsP$MnWKJV;xV+ zEy4;Quh=U@QIfCZlk3KC10*Od-%bs)#YjkN@@cPG8EXiegHA_-QHieJakq9}xL=n}No+!u`Pm!IYpKx4>g_ILJX~H~ z@DRlCM|fnQ$ES>ABJJBB@3=(3oZ7a%E(cBCMlVE%_&w7hA2k4ErB>>3@(DqM^ zeA4$r??Rc~u;;fXvPcWdbwT3U%eUR3n@J8_&p)M8s#^7~+w-H@`1lx*WU`Je@! zT3`D+X`s1&%=4tVOvJ=0;M&UcZuj1)11&4u%C*qMRGCy;_Dc6FZ!@V7B&7Zsx}Ep3XoN zvPc2*zPRAe#$r}I3uO6%uI*kSv<%D8C98{$ zfqD1I6fdmIJ8vDkzGb9skwR4Y%G5|NyhwHcSrEWWdJ2sBQbRJT z_3Pa-RDug#{UwO3`GspfBj+zoFo`SNjevbji8*c1R}Nx-6fvP-aHKiwKu2g^Cc$Gj zYbHt@rri!2Z*MR%Fob{6V6_^9dmXgpg7qp#irR-Ec-%wQ-wHEbuAlkV zVE%|au>?9wie&7X62}mRhDkZQ%xv7;E7s$qCH0MoF4C# z2Kv@edG$5e{3Cuq`}|cJX1HG9N@vZ%$GxeA^Q7_`@J9`jfc=K^&ptpSm%g-?enC}`? z)J%ZW1K&Y=V>k{V#@sCUT0WrP-3zbZkG%$4f7~R%MC>c;msS;{AC03)u3x{Dcf&6t z^QB*Ue}OX)jKBN=Sk-)&f`Nk|`OF970WiBw^kFG6V;ifVYOHY`YCm*~5;hv{*jV2o zBVB>%*k)ZTkixMI6%Asmy12b<>H`@^bA*#ofS_UEW|Y~rQ`ae1OV4J>j7`J(b31F% z?3Fx%$VEDh6k%{=h%}={IYlWX;S`RPPguYtL&?byOg|#QvJ)wv>8hlUxCltB)ZN5?_9jxSD{T&TyYis zDv(@D%TNw-JB^Ir5Og?uZQEH5^9ffFg69!wcPm4+x-*@+>*|g~-t;qmPH1|>bct3!kq5?K@L~g(kMi zKXBh+Qk2BStS3`^U5Pl;vq6W={m@@2=6=*P!jT6H)qxgG!*GlQ{|Lp5qQ;EW;u_oV z&jG@EmHe49TGy5vs(3mvL}Bv+Q7{qLvyoH^esd;=u;@9&rzs0au58(T_r*7_7m{%; zt02dImb;Q8HRJF4c6NCTEm-glp8KVE!TQ>dC2eT%{8cLoQisA=H|l2UbTR0Q!SPRZ zML4=sNIlS#Sm9^S`|`wH?T4lFx*VYmEN zi8hv?kN7D#_>#RoEnsTzE^MT@Q#fmj<+IyEi>cEdF!EJ8o?QN}8uOe0Eg;Z|(i9U1 z(n}0);%0yaJiP@+HXXpzy9}8`#H;D_S5)pg}j#&_=={^HlI0Nrl)c&yrk4-ayu>>aI** znYh)XnF02%>b}4TrS9&pm*X@7YvQ)shAfT@-f!@ye@%wVA7l!Atg9Eq;7}26RnY*;?$WnGb7q;W-Ld${D|nq3Uub3?cP_s4OIN9OSn1cURsKcML1VBXT0LoKGfBFO9Z}eu;)JRvBi;zj&irXAE0^;VOvJeA97{+CsGO^$tc(d4JkY zF5w`Kgk~jmSG@2#bT-GG9bUUOe5&AJg$M2Gn>%Lir0-*N=eBqH$im9|-0R~!PjE^e z6)Q2mZV@tz46xeM+V$hPANd-*qR8XlXCTBCy|h!2(yFM(k;4HojdPRU0wD+9C5ofm zW`_OV7EQfC@R? z#_mM+1D@yS%I9F#r0P>dwX-%GILxAaO@muP*M=q+wfk%LXX)h(W~~JF#E4`v&hJ7H9?&Bqnr|Z3o{Q z5$)P;l7q1d=N*`rVjS`LtI$3e?oO}z?x@r86^Cw`rOv{uM`R|SL*1&qwmSS`Jd0!H z*`lC3NJrlVNH40)@x-9Wi8CsPmisi#@)3=RS5;zKEIk~D4>yZxVa0Qf zUJv{br!njyW@(P9BYnNPkc(u%hY+=sRfo$mDzhO;X~BNo8baj>rY`DN^KluO)NRW6 zM?>}7Z8J#?&`1n@mGCkC)@b=eE-8U^QOJ~+9Y<*!@!velad>VqpJ5T=?3-mEy{>Cp zvHl9(6&`u~Z1n*%mVoW!rSxfKV?ODnnVD9(E}UA=yxm;#e12gC;UcL+s=>eQ9_{-Z z^9b3cjp`Xs)=fERU01&-BDiVQ2)!-Xpz55FLM5v9RWCA9JF29KD$XIl@7UsL1(xGBl9#oB`vB3w(6d zDQU@p#!Zzc-L0R%a2zhSkaBOITl{{=_*xuR=+0CFHW)hfTt;1DIg!Fjqb$K@k6ZqIk?hSj>Ybu#qu5$BCtZ$gO$4N|yZ9ru%!fhW$_%M4p^ zE^wy?id#VkqSZ5o0|UmsCbxuXi5?!H2T3WbEYxpDoQxOo@RrkkVXn13LA{XbV zAK4#ko7?7%T{o-Fb>Fr;U35D+RIPUI2ii4{L&Qax31a{HbL`L4)%9RZzWl-n(7al3 z+Eo$(HZ|Q(NhRu585o~SJ$;k-ROil%RfdPB;|CP#xGA69$Ky{Hy~fJDwS<6EL-Aig zRFEuORahW7kdJxAHJNQpjHq?h%l0H3A-|}SSZ^yfS25Fji-`ikpmq-g zLsLKHvv+Z{w+9Ngq{cUF-=U6W%(50`)wM($jHWf%M_!rjnF+PAUKpz(HdEba}iZFvoEzuxSO;`#kn#+nO-{k z(NY9i+@O&yc1-P^ct_IRhJOhB`SJi10_D3d8z0vv?nyExiFzf1bITlWrnOUQkg_< zaFL}xXIIz!SP>XN3M^=WE!NTy#09nn=+?9+6w}CPC{C~JVzO--x+Fk5uSX}mP?|h@ zt1-K0>)NT1(R%3OOD~-F%G45v2V05Ly1C7UoEWV!p}jG=kOw2X)Mj%}3tWaeXK=ZD zXo0-Ll)ml!*Gbw4X(2G@+L^d7F+s%SA!s(Awz}od_c#;>yrXAWSWVB@0~}4~Jk#e< zfK8+chH3(p`m>+}?JLqYRX;S<9&yOqZ9u^wcE(Ip2_Y=jODkUB2q~zry8YB6+*#k1 zE;VwW*on?HWRSnVVXs`3Y8o(PKkgm0@P3dY9DZUXnweFko$*Thk1V-AI2Wh9wUPcy z@XEQFIr!H(cvpVK3T*-pYdoc0N2&UFCL1C#T1z`aii{aBA>2_iiU&(OCklax8T8fJ zeu0m^S6jH=`x!IFi?#}+@nh8s6zv=6R~T|{?-yF2W)5o{&iA><|J?V8&$xd^rpmjs zhqv4LQmxDn3dI}diL(izN=r9%51r1vXDi~o|B3#QK~byiCWRyNiqgSU_a~2SdaDQV zSkuC*?k?{3DS@A*71X$>=T=sPFr&wV+qFc(C-z{V{i7T zh+8=Jo6X*@*rlH!@p*W(8ruj_U*hud03tgFRcvAQ<(YL;J#S~Q;3%Eo+ zb)5ZJK+`l{#mu+d%Bp%!n)D;r(5DARbU+|dG4y+O$-ltKb{-`W>fc%Ps6X$5@kWn& zgU@_J&9%YT+YWJ6ecEGuTg^kpt+mi|8+$YlJ^FChYxG29)jT3 zmh4FRxt=C@6EpHFZrPs77)>*c(4n(08R!ik2~*>UM8b1?$Q{{u(|jj{*`GLelI`fm zCAmua<_c{#aq+6{QBT>G^5ha$5tou}CzgX|uCA6t0=Om^DVGDI(&S*aMxjFHIT)us8=EScYy}Pfg;cmpRp9%Sa+wi9qnUa7zg$hBnA9MX=4El=k zu&bMZV3^Vq5<_*=uO|zoId3s9Tnh5w3zDaX$7wfkC|o7x57H;g8R+K+w~*>w_-V0k z@+eGf<%8*aIqTj^8@JZ!Rk#a195%Oaik?lQOedx2bd}K08;|iW@Z?!>QWZ#d z7iPd^=sOvi9*w>EKd)#f{K0Q<*8}vn(jXjKpC1Bwx?v|zb1`x)X0`{$7Q&u#&M9qI zD{P%Tk0SoKg$WF-d9}^|yj?vb6D7N*?#`5W&b}Ao*ivoP;F^%=Gi3rMlmt^=mpo&T zw((@$Za6*Ad<$bAW5_m;Dk%tv<9W6X2Up`WbYXDAq7rIg#&;~+6WZw3Bze4GvNSL4 zS@Jf?e`_vHRh^%wZ$oPUG@R#(cswdon23QFQ$_b< zs93jDb#w3RT~qgRc&P7JN7F_p@KZckcW!zaPBlxigtD@-7;MuvaKAJCQ8KD1JyvcJ z(#3a}44<29;_h=v+M~$Q={dsIqgiEUziwukRdoARY^U@Y@|lqD0m>K{p5DrNB|GM*HSdI>d7G;`-qF;3mllz!ob9MN}8I zxh1>TjVmxS0dhWq;FJuGr3IDVZSvX!*>Ci8!kV;okh#ez>}_dEaE13yO6FZw$FyKW zht@K$dn;b4L}4>B`=0n&(3@gZ2B_}_g4E6C$UI`Ar}6@OTwXxsuar~?=&oGJpNl#< z7TW0%rzA!0c}5-Qv+#CxkAEAuKHZ2s;z3FqPB6+Zq;F&Dc+AW70ap&bxjP#}n=JeY zIiI}ub|Nwq9fcM9QzDW=DJi=Ouf;z}kIuNnX^lRGohs|fWYMy1qLN=DBo@%w;D^9SQsnJto znLNcs9p@!a%a`Ge;;J+`^H-G-vq@1UG1fT$8U7|6>ewZtbP4yRvAhv~$KbhfR|a~> zmr)rtvylACicy%pzku9ZM52rPR92oXKMkJyBK9WB=^j5<0LWKUhfa=_svLymeA7L| z(yvK7IXm~!aCrXQ{_flB!=lh=3#Hw&F4_^-%rOW$)M?T8*6jEQJ+-vx9*}i z`l}%C61C;pxC=rLNi5bbx>S0$VvKEFLKbci?{tP0_$&q{3A5Rt;}!1O3v&Ae%-7~(BSeLLYo2+-12#mG%Gn= z_fr6D_#<3V(y%oyao1ry^ z66LXNKXK*x3s(VCNdsbax7P$|vhm>T)NPqFfixDW<7~{382ey6y0CgH`lvPoP)gWz=PHgWL z*bj-K;3T7Lb`h@7CyWsL+WXv^J9nG{KyNj5x`=nrzWY1JWp$}wm{t8OC$ka!jZ1n` zNk!0l58bN4Ne{YU+legT$NeZ zFgzj%f(l3}Z6HX9pnxCa{cS@I7AgR*baA?UxV<0HafdkS=H^Si%hx+!#+>S8! z%=13q`~K@c^E)H^+OcY{+H297cdHq-WuPBvU?MG18`xLMgz}9L)FS4B&X39{`vliD zbes-8mF4?^Hx-L>rsVv_S~z35>~wCQgNywF5@{~@_a#17vzG2uQBt7CITb5iEZuaq zPbKzXcEoeYm6nu^$0_@cLFf`W0f+*WZDMGatsv59?GzFg5}Xz+dH99eO3#=xUnQ)( zNt!TlU+5rvAkq^Uc{#&g391vxqdL@c?y|-eescM!Tlqn^EYIXdEWI1RfAf^FEyKyX z4!D~PdJo#Brq>olxM;oC<|@YP{3b>!1Ix)}7+wU{?KkUM^Qu1^Adu?76!vqM6By)jz2J65`H6K7m%O4E!Z)I}( zpb-l%pR=Cx`VhG~AlrAbsHzvmFq*;0b1zuheO0?$tKM0jDjSk!#8IzmxFKnlut(0E zv*}UOH)}V+f6$L(k3JbHIzYaT@uSVpZ||kvFRVA&;FNA6)T@_h^<38L{@S5P65}W4 zX8q^AZ2n?~)Dsp}T0F?JaS^l4wxAJ2jz`(DEzU2IH4+$iUu-TGa*>=X5DD`(-P&Jp zDa?l{hbt%!JJdiL>KjNy{m@VvyuQGr%cxi1UzK*sMTv?Lqc?=XdG}qYFP&|ekbfcI z?%UEO%Ypr}vNII;Rf{8j6VH5f9@AtjU6bekk+C{iLNJbGOwin^W!%GH0(ou>M5@Eg zw)meYbZIOHg&daVr~#5|ja;4X1Qrc=-(o>TK<4CGm7UxGMB9B_FnaRq2&S#ANQd&C zn4zN9ZZwy?@D-DhPK_KdPt)t|xzJ6DyB1e)$MM0*6;Dq+YqwqZejk+6#OoPb4lBli zO2n{6xQ&~U&F=Da^+XiU5UA{VN(k0D-eXI2C?eDtf zL4DTT4hqJSA2p|EM9L@`_3V{*XJfT-<>76>owBgA_}2W7CI`YDoHl)C z@*~QfLLG;9;Plbe(Rld09TL7|Cm+hg^Fe}*IOtE*hJ zZlv?>C42K!Ota-X`_MJLqOREcdcE!kUM;;;4ZwZRHyNEB#`qb(JF{`oIJ38IuR0yE z>~~&sr*Ce;)^*e)mCtfk)M%@YTNU35Jn2IjfVxf0yBduRtgOY>ZC7c}1Uyot~-N=TZ!^k|@&&3aCa{d#m% z!HYcObdPUs2d)D9Q+*WY5;<`Wyq(@lzqGnvk&e+)zhW^$&T7q~N$8rc~sKdl#yg=g=g%Bhg$mAiY6NgShal*t>R zXCdntnP94!4{jqJ#oUT8rrhWWsH6csAs{+|a3pxzSOCP{S@4uzCo8d83VN7dd;*zJ ze)`*fo^^N`N$|$&WIgSL4SsX!l6Cq!`M0}t8rS?5i`MA{FH}qvF^+FY%zU;EZ4Vag z)yyx?`TAh$tgs+IZR|nM*d-s=g^|GgD&>PmS@*>93GrWQZ%=%1lmF~3Y-xq+Syt#8 z%1R3E+Xwn*CqkoUp$T~Xq*f!MmINjl65JKSnYX_$Y+kUhhOt_+e=GvBS>jUuk2kbh z$d*eTqXORUOG{-Dca*Yi?bQyp3vH`dLcDEoo+3?Ta;-nF2DebLd^$_7x|`hbwK_}RO(6FUR}s(d;^F|gJsPnqVkPM)Yz<#u?!G~}Ymfgtv2--> zvb98{)WTN!&FM{jxI`Y8V?Qx_))n?G>D~Ql4}(U8H1eLF?TEwPK=0%1ppCKBxfdtP z*p}uApLbg-iE!l$bz;0ZBT=%GRUMdQ=gUsAlV!uMv)>3)Ql0hE=Du&ygl2TG$oCZN zl&&dM2}Pp;<92VvG1Gg>TnZBS>GFGS4hx9*M}zO`L0 z^$_V5H?w!jS+o`Eto5ykX9snAU5w*N-cI{t_1#WMYPgcdwNs`IfLwMA=!fHz@>%dQ z8j;WBm+JiXwhG~&eRRvs1}jt$pX%9bt!5q!`kqnWU!Ck;-5=GZoV)8-yI475u^J-? zr*m+1PmZQ#?QNN6Y0I&u>90C#us1U&IN?aKZ)`W%Kq|^-UN2~foC{pOXz2DMns&Di z)ko1t(jmaFu~G*e-?~yg1nt>h_*Sg&3!uIlw9LAb+e@Z->m?Ag;VOF_HDvQcv1rQ^UQ%VdP8#m*GQ8Pj+7L!S-m9jV zZ@XDD7f>>;T{zr_gwy0-BaiK=q~Q`<3XpV9sWsbclU|740F|22X1XPWqx~T1jcVsQ#mT+zy|Ya$t*vq zu4giu*J*CRVfSD?w~_8m_rivbOWA`_1zpqL^-EcmF3Y=H%^7{48C-Zi;!C^ZDf&P{ z(Y&Q-{Vd8ohTGU7zG9`IXx~wM?!xH%PgFqMsllPDy}Mboq#5`x0>rB?7a!CBUr`U{r&3lHa$C3uSOW^~VH)uJr*D%pku5%}0>)qwe# z2e}t(H{lc#1I_t*2r03ZA)ByoPD>G%-&5Dqs_Bv_%=xR8ZL4z2_hf2otQXE2w1>o< z+xK97IX70S^Z`W8tbSb5$Jo+&1IwifST3A0z=i0W<*qML%@-M~qB&k}F;{lzL)8Dv zhlod3hN6`1`%ICfZJh7Df{C)`3E{D-lS}Wo+cp)8OBZ~lqu%|&gKD>_B4@9V`5w>3 z4L4K`Z50`_RAXC8%=*f@5IR=2uVDB}w}oqVy2#!O7r_gom6+*EgBon`b*0t4z7<*) zAvl@og6Upq?X2BO)~8_WJwe0LQ3^h^UQK!jY7X@nQ+yxgpxdVeh4;1tmTydU1EameZ#4%e5 zK)o?gn62jmC@+GLwWVo>UIR;5siL8phaP^Ghe3EQTx>)KBGO3TXX?f?!YS>8*9m21 zZG>2e9BOl?kly0vbz_6i<}E$@eb!@q`Ee8^gVlBty4kRRHbPxF@c4)BiFo(i?qD-hmmI$5s6K2ndG zHcYh|R${<;r(|aeT-RrL$0i+55%xdaa9ADgtF!wt3Jk8)LK>r;lF#{C>g_pn97D`{ zbEb6ssI`Inh+c74`YZ$EPZoc)2$;k0_-y{|vM2_L$(ao2*EQ`ZCSx1WC^l9f@G z&@;5(W9FNAFd43X@3MymK56X=JnGzOhF%tJljg~2fo#sn4-fZDuX~2?I%_&kJ*p0G zoNbBH;lDbjENq8CD38zK5cql7#VuP(tkiY`kA5R1v($|3u-dKR?ohAKs4LdWO6(*T z!%AgmHe;8K^Ao^D&)wo_Hd6$N+NEV6YUeGXu=+}5+jSl~!$j7*r?quj!Cy};SM6z?a-s}32}z-C5G#*gX)4K1!D)Fw=Z#MxKQx{Rf;>gs+$|g;c~@Zx!VKxpY{$QnkgWc zQ<<$qpERc2QA_MD))@xThHPeVU^A6R-sQR_bJH%!ZNTp0YrKW+T>vSJ&Tp zR=h1|#mu)P(J|_H-`ClDpXwJnm$J7qTD&>-odmo!?NIdzJgA+Y{@|FgG>71JN{etw za#l|Hw1ed78% z#{plD&ko8_T8mqd%Wnt9j(=)@$3~lt+Nv?CmrFwpAg#=Qofnde*N9`+z3mhXdP z--z1a1`sRog>E{tZti;tt`{Od-Vm-{-WYf`Q@Ye?XWTXw8BCE6*QzlOM4y6tT#(EO z1DfBhPQn9sOV5@0xQVYg1h{c;%ISB;ZfH03=C;xn=Zk4+&J7*FXXD1QWv;3`9_#3! zDb8rtfKy11uFIV$aF^6(&REh=0#bE{i&5ov4e~IhCcgrqm_JHdSkIZizB&RD?j}X! z^6uOM+PTjfUMQ!VW3+Goe7s0xVZ;`XGOfxef4`^0qE8^jWK84EGo zm(iGR$MQBcox0wF(if5zD;iSnB0DImW&icIeQmmg>Tk_ra{?e!O^QvlYVSNP7UNFts=A#n2cw4Gu+_G?p0f8^QwY9 z+U@RQ_{Rfw4ipI}%OXqXrK(1=$aItxYRkg8RKhWCYiM{=Q53gOeda+gFoAR6G=FBN z6{v*I*X*x^Hr}nTH2SiU*~TJTK2f{n3v4`K-+KHg`7G6Pg9J)Cc$PaH@>qU*D<-N5 zM>bc(+q;m)5MHL9U29aTpn&g@xHH6Eb5OG+zS_Rz=iP5m+UK&S7d5lTYAFn!$OrHy zLPh1)KvB8o`Pej5*5(&Qtk~4D>g92qyB8i(Nc@Fo)G(S@xRv&W%Pkg6R^3DOzJd{9UM?f~k zhgMPJ1Y-H^S=e%^ED+k(;c}u+MB)IT2F}bshDz$ioWzdBUh~9bS23Cgm0To!1y)Qo zL0m9gwe?@Z|B405^2Q9n-Ot?rWL|C%i2nicIZ;q352`HKmMkan~F~aCWHSagKZ7+@AHz&B#rdy$=0I^G-kr};plidae7<8 z(KVluAg5jfFmfaF)EzABMrKHOl@!zq_1>AtE8_052l2TtD0d)ctu<6dli6{g=r3p% z*vvfb3KeMlavvBJgvL(5eGQd5eCAP;lG^W6c}(9S}+OKORt0UYI7%8>L+t64#7JDmhjp z%N^pOzh7L=BME~orOSQ80ieB+ocs0qdPHQ~LWXigvR0z()l*6{i0>hGuQH-!opHPGecix=`qW|-_I zsz=FADrZe=OmyY|uHU!;Qn;Q_h6JZPkBGE`0QGc8^I74WA}+Zx3oR6x|s!1|PJMbX9- z?F*4EK!mi+NZ)UZmZMCRz8fG(==>p(eEoOT#|Bi#ar>e4-mIAfWq`P+ojS#rt=fCZ zSBf>h>aoBrN0WwhNtztP)$aEFg|ec7{+UU&qyW59<*|K=m5ZTPWybvSG21%##A3eI zJP{cMb^27U3Jmdd%S!Bsj=jl;hwN+)UFjQXuh`qr*y$Oa)XHkc3lgJWS$NcKyYC|7 zj)^I{-Npe4gsZPN_gJMTz)sD0BM+4{P$#-sT1yZpX&{!+lWYTw4ftIZV&As}d3y`W zhK)2Xku_0O>fFAJy}FFc?rpi%BWx+D1%$q*h?w$3K*_J%ByST|pHF9Ba3MgU&f?Po z@0w^=L#^TM8>y#hYu61!n_iCY>|=L~Phk(RzurW@TmC8>YP?cizei&)j)jfUqhHz4 zB)0=;rCR@%M7e&)_@}U>uLvQDt&Ry$d^JU~7y1DGuKf-a-*a04U zQv)P6_W6mN#3NNp#!E@nz@If{P5Ga{=%v*9*&vlK+5jY)*OpV93yWe#|I zehiW8)bp(=^owBMQ_fq@5{AP`+MzD2{$F;yBXM+LOo5Sxhd~&2NGPWxK{a3-$O~za zk~)-`5P%mX{jgHoHgL+O>Uw1DN}b01E4K+gKMLa6@b@LRGMjdh>YbSa3+JWS z)obQ+G^Tb5s+HhUr_V5U1}{9yZklNnAKgnl0fOWr+BjAA!Yv4}!CzpkI_N9sMz1%_ z8zZ?Bn$1CxPtaN z^9cK${hiOK+4s5bu=#f31lS3-*3Hjrpq~?2GfUGDx;Ly`b8G!EVu&IIqK z%)g53yWoXOCns-~biD>1W|B2W2x_x0!Nt$hgOq$CA^{W$%9?v(Gzk`Ra!{I&?s<)+ z))Ipar#_c#XXJT$h18}8A*a4-(v_1R9p-3T%-fitJTJG?Y35$4_(8poWI8~482cV{3=nF72v4%AsH}Z0@Ho26FFI4e2VW|kNt1(+ z{upd9gG!0!vIsaf2k!F#9m0S9N+fv;4E;=Zh3zW9g)q=v(G#$RTi_l8?}Sciz+jVM zx|{!WKClQWw8#bV1h5B@If!m}()mPzg{x=?#8V~hKY#T^WEGqxKv-N0YH95a_+h?D zvCkBX0m@N215z-*GFC7DcOM>zp+v4ji*N+-L4ptgl+Xy92Tq60%Nwad4{gCCO!te{ zz|e7U7BZipQiuchpmybL~E}<~%m|5(;vtR9!+|!UD&pbQFPoK_6e{7e)i1ep0&}hi9i6X(>ligRo{S`1X5>p*&V>%40AHC{^c{%0Ng58t?YlsGjK=EwAcD+0OSS@!okA6Mz0WjXAexv9{2vb6dEC{ zzLJ3Q@ogcILS9dR9yR>;;ZI;OkibM2iwuo}QPRC^!-r2FXepRvVY6{a79o{jUqg3K zf!m;T>}h-z1BBnGZvXcd5&>CrJ#S64@7XKlBRa9?=0H=kf7~@w()&@DoV+53?Wzn3 zXhi1%R>od+7pffOEI*z5E@1WA&JIY^i@tJAFA%}2ualRZDBgN1{{lFvuL zYpaV5@jIDhv7%IM^#u*)-H4IF@BhW>If3<=7$!c|n!u7BpnFmla*YlV6Vt^g_s^Mnbx#D*)E2vsx)Y>f(Ei^TC!}c(y&nGrp2s4| zl*ohp1}{hFoW*r3T)CC-!(k6UVr78p@?vs%dXeQAH+xYla$**7QS%+X@0w%S*zWSl zl*EJ*4QGk%H{zS+gy(B-JkI*8uCg+0NYA_@ddBL&hqo1Zg<0Aui@^e_U-3`K@nZa; z!ocjJYB*N>LQ!yPJEE*$ng0aMH89*a@jOxKT=sW_#y=zVZ)RHpu-4{liYWgDOUGAV zhmy?X&qg)=R}a^J`g|6uK|d3W{FgHvhiP&au=Ct4DsCTonEz?IqZ=2Q0cJFxZQiN> z)fojDfU&FmDWmB4lK%`^Y(6N1+0BsaUrFoeEVI{v-yxCmM)1F3>gZtlPB$>RohtX= z49jCnipT>aAKhbT&U%ajSS25htonK7L^);pjKSnL9d^o~ZjQ&+fylrCauHYxpkE&S zqZiA~m5NWff`i*qXyEEjq!B!O#5461APQ2DGv!>a+)*hB0%^b>W7~>`8U_8=YVBY( zr}o|$$mRWW8WDQPNkv_xD)!gE{Q4m>2QVll+`tDMA%D&1yAM17^P%>=(*Lyr7_$ai z%``V@vK?Ez^AT9%+(~}Hza}~I!wW*Z`ltMoqn|KA$>?rPY90SXWM6=fjmgK6>llHH z;DV#`J(>TLzmMz}T526??>Bm`HUxS)`|~KtI>0+LMJZqVPhvZ{8vJn>t$x_W`7d~t zf*2o6_O;xhbpG-MerGuKcM!zr|yAHh)33c2sQmAL7^;;o$lv=+rR@EbKC=a)Lr{uC{%zv z;ZM&zH#xe*llKjP+^$W}Ab)NC$R5lQwv6IbN9e>A#qHWBU?Lo9QdtnM z`z1$w7s0>wx79$wkt2Hx7aJ>{e|23Z{GJ%TZyj0m`3PBfZp}_+9Zu(JIgo>82|cHe z-oBl2SfR9=ug)^E2bFo0pozn-sdp?R%Ox`08{R*aoY}D0iP{-6?U+NBTk;%V=>FK+ z{HxId@tQR1+AadD2{M?sRaWy|{R^NZUx)}u-Fm}&{9?Y3O)I?dLzhv4nli#wkP_$f8fg7ngKO5s1vgL7|FC7X%{z9(;zHdt| zV|%`ME0dV%`9 z-C{TulIZ?preMJmC*hbX3!=w5YJ&E6MP+UnP23I;@y6;~R*5N5^+slAL%1`hd$e~n z@FhZpozvv?aDKPhfiBPjlxgr>C_I}f=onzJ>x@Wm?&=o$(eLP<*+waKgbGezc|nhH zT<#Zq`UKr_7p z#e;|miZ^#jJlJu7@@!65>F0pLxlwO=<&S||@!@`-(EihT5v?-V+Dq=l-t2%&cIph{ zfi1M2lYIeQ)cL;veMq=zV1vOnlm2y(jve@@y)f*a*tTZwtD^**K@#n<%cw_zymLFX zN_&p4&xIyOEsb-oL|!$^)BN+ZP}iE3s=`mlu-MrRpeZQmNjWM6?(GdtK4nn=iBreM z+`HVT%Hdi2b4?QC!RO9M8?d#^@sVfc)YtRWVx-75(D~L z?926H(yz{S5ifI?T)!W(YqE{WJoK^rwcXAR0M|VM3}ytXE5%m|y{*XyX`V-pa{?spZGdA7 zlAk`{K0qA>fC9O%n?C>UaD=Z`h1o>$dNw0QF`rgHMv2$Gf0M>dUA|QP%HObc2PkKa zzS9iGAf(Ma0dp>r2N{74>c^B$jN+l?%9#@ZB!r3ZhH#;f`W0`bY&kx6%%!ACfkHlz zCG`teK-^V9_8XfUnjGlju!koR3vGZC(8N85&Z3{>y`KEuP+!R4MMfs;R)>YkXy3Co zcGs|63UP*z_Qw>_nlB zclqRM*SvBlk$XE?eKZSmG)N0ii^fY5-f%;%I~>PEG7%FDQjT%OOV)rk+N@nOi9QXo zGM*fdKG)L!Xjt{D^;B{~d`H5TM+lbKG^i-l(lDzRap{~Me;CN%J;pkJ){Yp0sj653 z_A&^S?h*>6OvWP4f+;0k%DX}3>bJr0NDtAJz~E};l-)|)a!WQ*sUpklCBL`W+uK9TOZ~9E9hp0_V{%u|;$I1Qi*zM|Ju)q@e z5())M`EO%)dL5Ow>jCe>n!I+!2-4PKq*R<&BDmox2?Wf>2=imT|+|Q$I0iDV4|(fx$Oe; z+2Xsd4-xN)Z;c*Q#;w8R!3&#T$N94#Q>klih(|VV_0UnH@HT{`K7#ZQxL`wMUad3B$=J zyK~z>n-1igZkwaPCAvWBx)uGYFR@%@N*W2P%f{8T$!&O_(n7mislWbkkSpdG*zic9 zd?un#$;88u_j3P2cn;1zu?$MNV_tF*AA;6{0mdZZcXnfe5mJj?C(n1BQ^}>%TYjAu zvf^8AartlD3zei#@spTX1A0euM-{sT*lFOSUdf|7U5|P{G$FMAQc8v|=X{Ylv4$?^EFLhr#Ivgm&{}HqUgO>nTocpk`U=AADJ58bwO9uYLffxPFgNje1 z`oUW=qOAJQm(_BSbh3#$yjBA{p*3lEOF#uXhA#zjFj;X>y0?r1@KQW|q9+8v`z7e7 zNIV3;FM>e8e(`%!uFAOK@Vr?W<&0{k4BunSju2|brtEI*u?aF5Pz7x!&(R$G7?9y(8FX zCYEn3;Qd#fY5)x74ty7nVG^)vjiGRnyiAVw^_(I%{z0AKd5X-!4frP(TTK5)=fu9s zpK$-rt$$;S zn~;$9S+gd{A=rO6^dZRukNj^~Q4jzi8xMX(?j#20V_BTaSAoH540BD7q2(_f?B|bJ z0`OVg1(O77fK{`{S0s$UE}EZADICZ7&*6XZAyXm%>1v~BU=?)5l5MQ1F{`MI6d`Z@ z&)=;>>67|rO5{R7n%?cIH&;0E!nsw}^XK8^`WFZ`gZyQ$7`*V04*&2u1-SjRAJSjm zCnn~R3;8-zle<>H5;&}r|95o!f+W#wI+&f&DWTdAfY9CSoqI|)2v{X(2-^EesR-!@ z&yUpJ{A1t&@VMbHEArM-B17A)3Kls3hW1}OO)dnygK^(- zsyn>kN>!6@r=*hPGS;zN-mlK*1M0O1JO1uMd~>`l+TfG+piX|`R{!sO^z=AZ=Bl^iGQ0(^k{OS4}(Wx?Pq&+7Vw!0h%G%BCVp!tYOzo_k+%3}j`1OH`%gfm_)Oee6WNGlxWjN7Yf2g)qa z&qKvkghq3h`u@7aef20{-g!#AvjoGlVrA|h8;Qf@LSY+=Gut*5*IaVUO1xV6`hsHD**Pk;bXMe5$q$cMiJpjj$3{f%yBS#+b$`^3 ztP|t&+DkMEPGqTNbp7K5p2#0qL{I*}rdR04_MVHBO1Pc&?F?d$ijGHmMST(FAoSOO zKgV*=+etBVjSpPdQtZ7!&&<5=t_Yql#~PAiBA?6g-WBV_D7GRutdSnFnNyLsdr=Ov8MgLWl{M6Lh)YP>AiV^M$Cq%{0 zzAWeD7gte$LfRj-B$-kY*8(XO58b>6bXxfwE~uZ041sIlv4-{wr0@of9bU= z*3@<8)S>(1>@uhD{+gmWK%`2YnQQHoflE*dFRPe;36tUM&WSkjPE1~le}VAuvaT0^qMeV5YeAR0D&!8 z05AQVOom*%4y%6moFZMcW|l!hDC#Gj{0FlBmwf91$l60w^o=LZr=}L4CJW(ag+SH< zA9t5fQL(>d`kv^45crp`p&VC^;Ka-fI&SREz5BnQ2ThJgB%(sP_7?o-Jbhc(y+0Hc zl`dZlv!`bckfgUeiACfe0%2YQLGtUoA{pRZD%o#LhYm^jFQk9Pqi4o{MXRXz;K^-y ze6R({S4J|8zA&b1zVFWhy5a*nNHvs{Kk`+C6j(4rJkkF6t{AR7fiYcm7Ztm70wHi6 z&^ZIcTaJsksj1P}SUdpOvtXrw5C*{`l%T>47L1^DYdrfG;vjMi4+7hNJxfgX0{a0f z9h@~h{6V7;wSR!YdAuROfT_?*e39$ezrIq)1`DcMOkU*qb!32I1x~R2d#Mt?0rit6 ze*@|_ML|6FoArLnC`e!VEzE!4dO)H1tseZ=qYh1*|93sgSBmk+E-GoDL-=6)p;>QU z(NfidctptaRiG}C-3O}U=jXA$_x`{tx^&ZLAI@#sZqSj$R&HjvkPjEP+Wka~H+g?7 zN*}>(*vL4NVp-5Xltf8{i#;t!gy!RJeYBK$9K z_@82g6i|b|DWDPtCWdKIu%g4pwS$8rn3-w_J$4p{b=TT3tW zm@jF0#PXsXceZCDMmYFaYJ9oO5inX)b}te(YF3?!(uWeg+P1O_oheQqmdsuY`_c;9 zl&sVrd^La9&d0X+iZP@)ceOPp$;}|Wh7$Xnmd_rSb(rip-QbuhPoD<5U&k=mq`XT! zKhLhE&9$_-l}NTY zK5uKdE6Or61Ba(P`Ozb zrw6JHXJal)I-d7=!&f$~+fL6fo%gq@oZ^d=GWMZXALUKi9kBbP*1n=C6xp$2UEtm9 z)}BBZr$l$lc`?rUGa0C6erKg-*?Bf)uxGCKDUS3~T4>Ja2Tt5y9Nrgtmp#fOU;gA7 zIe*K0!07zzo3l%BiUC|YPwQCJf`^hLMlC;BE`G~u2 zHLBeg=Ssl}z$jx*+T9D}iaScD_s&vrca6B2rttKbCJDBy>DQVwoJrug_nraZb+QaX zu1GB73t$_hi*jC@0M<{K$k-`hlhObyqN^yK5sx(R{e8k(Q=gr|?vc5T(fTl5Py0eG z`O*Br{Pt(EeTklHX4;ndNX~&Rx`G2tAC-yj`>vDnY`b5|2XgYuY!dn|vX z(RtU8DW9dS$xraGfj=GWqW8fBaau+e4gyPQL= zjC*dV@zlAYs>Y8<(dLfj+>qDg z_us4K!N{+iqOz*@^59=>NU#nSv`FfUg#wSSnKIk&y?0k< z;vbeqUw_mj$+*XyRWUp4QeW(Iup{O%m$99RGpln=5zyhX!O2rrsuQmHBYr?SMuw-tsIwY zfQ=_5b=8pboTwPbX>1#yU~~}!MA1mQKNTs$yB!XjtwCiDOoaN(o|(vqk}GWtwv_W) zu3oXO-WoSeFf6EEPJSB>&so6&cJj;md&FeO_6h~XH}!BDXER$+>*B6lCVk(0^_R-k z#{8w1ROi~YXpQE3cr!aRtOGxPP^5J;qTolhZUt6R?vuQ~$Q%cVr8&fBO`=fd9%JIx zYp-qN{r=0UbrSVFvT-IRZ0?x3&_zO@Xsuj3t_SP0X)dM8^21ycQxM=zr=}X|ltkQM z4wx?q56Vs1N_*`;9j;8vWvtuQ7v8lL!hi1Tm#yo>2R{3#w|y@-%g)kNdDZE_=oEV@h=hRr%ht+n9G}zED2TVAiZ>w^*c0W%H02O})Fw9QKTy{OLv;kFdM( zG?%o*(LW?tDeWhK?cgv#r;)+1nYl#?)KJ+17UJmQYEV`D z4BV+VPcx`Iy+8iy=CwnM`h?I-54A*Do{_Z(w`haQpN{p}x{J!W z#>;CKgy|5jF92dLN}n0!*~+o$VQX7yOjW(Jd>=*g(lNeZxG|W@xDBJFAmKz!J!N{m zFZ;3e%NPlYg(}vFBlZ)neGT)|tak3lvE0^S zR2&YR+gDGje<)t*>G^|>gdq^{{Nx&Y(#rWVWLc%bXZEs@m1#b|ftP#j%A;$l_IXRU zGFjEC*a;nN_0?I?LE@XGFLqDO-O94OrtQ2?)!wv2WcCN-x4I|xH@{WS!Lrb`lXk-w zB4{J3A9}TVex?CV$VcF&u+XSmKj-_BUcvw72SFdW4V;hQY=PkRWLG9mie;hK*F+!h zM9HgT9VW%--UU8C=h@By_l==en_4ti*zCiR8rxc-tF(4FWIQ9^`nEUE^3)h~2yVV| zX^l{)eFzi>dp*$NoX~o8+_>Gt{YehZxCBj^;RnLhR2>n-Gl2*X_w}xWekxnf?~B(W z%vqZ!FP~^hzN>B87D7V30E%kEc^&J@>-U{v+hR(-eNBA4(eF`kUaNJ(RmaQ8*0S3K z9n|tsaKvkM>>0fD6XEEMi3>}Z`nBen+P?h_c&%(T#@HRk>$Y|fPqSBI>N96t(Y5XJ z?UA6LWgW(19jvH(LL_KnvbMz9&c?~y^a+T+Q<8KAC+Y6|&|@@wX4$Ng9wb(LkAk;@cjk21LBX~()`;_<R)+_3TTJctRc}rweiKFE`MkUk(a{>g0cmBiqEa@(v0;l1C`Et@tbCd9VSe^&Z+tRb;o3HN0$*dFDe07{z2!#oXU#P+l9QS z)~1@Mb9`n5RGdcqJAL(uJP}Nh(NjUUoZ(rCu@h}#?(4G(SHi{?3Vj|$>F?_o(j{m+ zZjXK3NIKXJH(zF4KxEV=ILe{pP4;(J;yKLZU^^$pYFI;?X=&$d&w{q~iZ^k%zMOCgye7>AyNTyS&tW=q`-m5i!c`scrG{MG@BOQP0<)W2Ac!?w3l+Aq1 z?Kv8%TPBecC3U#`PFjO zDp`5|e&+D=f&84e_60b%mdw=0gm#8(+=&)q$?0sWtwml+?~9#e+wzCg{;=#CU+>Pk zgB3du$r>>olvN^YLl=A`sS|TO4*<--B|icRQBZ874JFqt0hZsr4w7`N5?))B>1%y z00V=^^gE1O`Bb{k?!-mYHxf_tWx36DZz@xY(DOoJ6voJ)s}DpR|A-KFlubpgPWX|s zo>BLRz(=H}TYgx)|DrS0LQbQPmte0p|Nmp}J)@%9wyn{v8$|_0Ns2@XB1HxP$*3S1 zBxew$KyuC$C@2U>&KW^+lAI}#TqFg_NpdKPLQ+u_;mtbt?z{In-+A|(@5gKH{Wz`h zqnb^qwdR_0j6VA4qpwoQflFMC>w2=kX{O4*@Egw^Xx+2Z>5(aBHy7Ch~tC&;-|?Q-+jjuAL^Kt)AT z|G9S1$W&KWOpVa&kNL^0pLI`3XxH8cnonghR8+63=s!$Ewc@&pr3VQ`&c|l9x2ou? zEHer-Qjc*lJ4HI>Duav7Hl36d^2{z{dXdmLr9MiXfq1SaTX@Nn;>I$#AzBuzH!NH& z{5U{LXFnd*43R82dNqgCZynD?FrV#Z@;AGaG9JCN-llFGU!QcTuDkcljJCHyx8n~j z0Cdrnzvf|eCN@EHOn_Z2+6SfZ0%Vk@cBcpYinrFtOhQEVyGAxlkiSOe)3IK}w&)e3kw6!5as!8! z%@R)iFKWG)#;ccxB;Cc2{8p1+rf-QWkZ_GWwdOpm&yyh)Z^on&h>YaX#;UcNE%yYY zRub1lgXNn)zN1g3`=FBaoO5yWhZQX=^=mpMq$TXRoXF#=j4ag+=GR=BX?Hu*crss+AN!;h4`pQ; zbh8zIeET>>QI2@oWQPuL2C-f}0wG>n)?)%8dzJ&o!(`P+w*h_@nkWXNym?nn9$eK; zTCZgSAIxky-;Abv%>1dn4!UOC7&mjPujzhU(_1tm!*jxPsTrHGT6%hH2c>qHLEPeN znB*E&!H~V|qKR>W(KvMw_@G;gTogU4M^vbj>g!i;Q&>6aQ++te^ZLB~c+THHx>q*j z{4{K)lbph8q~o(G86V}U+jVwa#A-VtXfZP^ERzzM1BnE9SU#08h?oiuwbL?Wk(e+G z6Fh{Ub2>{uDD%uSs8inBEXI-h8XcnH@dw-DUDHY*`#(aE)DNNZ!(AlfCtuq^rYtXz zJG&&QNNqoXP!V_2S)Gp%b&#hG%uv-0k&*P1z$(;pJ&vxST zbdO9-|6@!y;_@eDBgAH&t^8xBRhGnkXX#W3E8BToVUGjCIX5QzOdVPi5}LqeWa@*` z;?y0o?GY`JXh5D7DnU{fI-z6H0RCH~- z#AgBr)=j`H7efcD0Vp* z)p@&^;Ia36&-Uwtme7y1{ScS?BZ1!HpJF#Iv{FXxf7Z(*9F?VS$g?qm)5 zxc|bKOXlMYD38d~LAKh7Sl0LE8eU|a+D+k}6$}YEjcG)b9 zwd?w@Iqaw31IVD$=AjTXUj$pEClQy(&>7C7GK{;d)#4fjMd8EwhBb)=Y{{hzP=<+y zIpSgU>Egp0dSU0=`OKjNI^A?Wa1XjpkFRgq6XCxnzk*~zoghE{E;$*MWL{d z`O%zxle-)3YKsX#&$SUO*B-5BRTwAha$Yg|or1mglCyTC zT!gNUYx!xegdViM-9dDD%{Yzb#@V+vWVUPMT{0h_ry%5wdL$b*m4XYW(r9^KA`D+W z<4a%L$d7be6(hwS@vG6e{kY*iW!2>M^7(xtf68x7+jryC6vp94C}URKc2gk}WQ4Is zp=a}Mj6Wd_&;1-Rm*+g6_`J(J5pG#Y+1WpvAxTD?sSbXwi`X{8(PW19<286~_6diP zW81#glXo61+vmR`Vd>76WrdpiCTF{G7)*g0O!?~*z1DH)C`^6TSHu%mAbAynCm=-= z6@(k0!=WpdttL0x8XBin#&-{=>s%Dnkdj#RoTUY_iAzn59AjqU`>W*uB|6rmqsC@; zRd$9?@0jk}+Q@)mk&i~m&)u2=-F07(YI`tE(kp|6*Y8@=`x|N@)GDGUWQLv_?wRNu zx2Ezzlj_=75ooaxu~b33W!sy>9+j<3`{^6=ag5(Tn&nt>Hj?OI)n;EF^Ha|QR)>mZ z)0&LpQaL9!%jQ>#&9Ns{NFcRphYY5d+V*+8o8n5RTR9ru>Sk9D&(X>i%764#7rx=A zj*0)j%d-UUt{wqSN%`O^K5_g}WCQ%-G& zgRaP$Lx`&3g%G%5vb&4TuK|JFuj7Lbs0VR8a%UlhbT^7L(`~$8H%{9N`D8QYE&_nh z)QkztW^B24FMM3Qv-4uw2dx`?=)DvG=tSMZVp+uY7mWmn*|(8&8$=8pA;srFGWxO+z0ei)9uYrflZ&=k+G* zovM_-a*PLyH2@9>s-ZOO;;=ixB=5Egh|2_+AjJOOWq(e?-Q*$-^7hV0b!$AMxPQZ8(ck^v}6W zx2J2--{wr7=uU>*SGtqtl(xDL$V=4w5+fq^DG&`Wx%cyqKCyv1JN0p5sT4gw4p)?| zkQRrPcgU8p?E#8~Mt__o5rb3nO?Wsoda!6gt#$dMES)f4V9hrB@nOrf#Cdb58{97) zb$;f64k%NkE%B;c?JFz%l%arZxNqWpa2Eyj2R$4CR2yOOwn^xQ<=h1xm$!C}+cx~L zR%Skt^(@Zxp{G1yYDU@F0Q3f=TK|o|<9P1pv(tvz0!*R1@v!CY$KY%1Ym}FW(whL= zKDB(zb=6)XT301o7|uqm2KV#u;l}c_$)2X)=D^)<4o)ER`<`(&hR&5FN|SUco#U3) z{IpaBQ-x?GoBURY_Jv{k0$9FO#dD6LzrMbJF?W>F)a((u+8xCFw)?VUJ z9>I0(272!@{EqLG>pj=}L>oPIkTjtARXI7@FjeH>frcOh9CmuZH-a@}PbiQJqrq*9 zisUqREJd=X4s8kq@pnV*-iJL6d|!O(l{aS&bs8WKE?tm z+Uscjz4}$YI`mBD{ZGdHG9swS&U7VI(z0U^rh2hj@~on1Z!K1Ju)<}qaPa*yx48QI zNE)Iyi2JV#3M!XOsB#kDKg}(Q8Tv^*ny2;FW#x7MYfpJYeb!mOQe zJxp~b@up67#2=r+*4~=R$hd`1!%UKS99rC7@8v5D7VVeVApACl)R$Z7(u5RVgW8W_ z4Fxil;X_uJOK3m%iHmLsn!Cx?f6WeRr+N4+nNJr|m7Dtbhvd+~&^-a2;WRdS4Kb~l zv6`x(xpqy)iJG5_7|HX7^CB-p;!Sf(@08*9c2z^NgRJthJ==zAd9*gs9(Vj_)A8HXShe$!)$+ zeUc9~gCdo$hhJ zT0i(1STWV5K7WZ>2b4j6a?H$XWiJ_1V;D3vhNOHPojP`84nSqkA$>pM`OGzt|LtPkNqQTQ`3;Ov!!r zoh`7!d4*d=oAz{C0LrJHz&sv6f+CUdR2(Vm&$1fdrSqBDYAGQm;J2X3j2}1lncWos zzzNQV)soL44GK#}u8O6FZ5B2h;^rl{TjJ2!W<0Tx{@tzc^B3BM52R(L?WgTW16S?R zNy#7cf-@I~)(J23;nndD@q_XTp zVXOBXTCutO{@>GBUqQYX3+vkDm4v4hxBdz`)_<|Lrem4$s}4Y2lVm>4MvyTe21jo5kp$>HIE59`RIC>9K{M1s z4E5lCFKJutEf}37aa%+1zrJgpUpOR0bsDmAkl$u3U== z2%B%Hj8`DwiVm`YROG4KJSIv&u_Y8d(7nO)AY&It)&Cn~bW(@eb<+bZhxpl1u=|{s z29dMu_)r*k#!o}pcZP`~A&7#)v=kc>iAO&)kDpVXD6uO>OFybKJ1(@X45K`aitoKH zv_Eh>Iod|**>X5_TP3fwZti&4V(5%-$*&*v93zjJ73yGSh#tKUdY8tw-?Du85wm^g-?Dzh=Y8mI!_=eujCiInK>FWd z3K_tTy>`12`yI6AsaCo|Ss?1dzoXo*Pf_Tcc#M`Ig<2AcCNI$XPRxLgoGOi)0dd@Q z`P`NOwyyY~Gqis>Tb`N;Xa|S6<4~hSch5-49;>O@O(qPA?eV5cv{-vIx>G1fuh4f3 zjMJ>gUHRqW_u+CH=r$$hQB6qqe!aOc=lm)VzXt;;DO^$l-G}^t>vg#BA zZ>QwcUv1XnP>WW`FW7%)bM~$(D-yz*XVh4>VzA2oKw4vrp_BC$y?=AZ;Fpt&*&{M#`A~L4_6WuTLk?o-olweoZaVJ^!rO) zT6Pg3gZCaUJO&M1+pUSM-0C?WhiF6k^o*t{3Yi-zaT}sf=V{9*MpXUt${50UUtXRPoGwWvVU&Hrx{!;Y zhwvB7YaS}E%vZu{wKny?Uf0van6;s^d@nzrhRI2wPP>=;p4fqYDhT>AiGQ+V@MLSg>$Lk_gF+k!O43YAJk|Y+o@XOC4LGwWNnBM z%q{coKy{#osIXD7qGp`(%>vrj?o%KqT`0K8#+19 z!b`M@E%K00_3|q9cv2H0dgCA8n=o$bs+%yQ)u4Jzd@)`bV1Hmao3PA}$ucKPZHyLvV^Ci{r}qlvyf`h%72O^D@;!LQhW0;AhY#?5a*n;>lw4)SYK;+=7ljTd1gS z#~CTkLu)3VQP(&VyjL@@<8UoIejn-#OC;cDMkWVVj*ePz8VK^wqA8X{%W0%#+LUA( z-n!+d3r@0KSihT{T_7pEuS0}K9NZ-T?#_%uUceRROEea}9m}VyG=vUU@?LLx{b~4a zH!bG%xLet*x5$@FQ#t>p$-sT|6KNTG-<=vJ@@RQx#Fzj2+oFn2AvuXnkyrxm?ci39`yTO1|o6vB& zn9Iu0byrxUHa#d*<{aDE2Lm!}(nU(E5STXte9kfzJsTNMb5-+{Q<3Ra4)cR5Q@AOm ziv>UzHr`-dH(iz)bXB^U61t(r8YhXvGIFfEXGkXyd05x5Ei+v^(I&Tod!kpsBnRrJ$$yCne zwbRlKmlO)f-1A7U;XVF0!g=j(SXv51y-vSl!m3S~aBXFN2sS6Ii&x zYI7QI17TXQ?P~v!80K1Uz2pzn<)QV|MF7P{BHO&Htr$60+_E z+h~QeDQZ}1uZ|&2PSWF--m}h!BO1ngNLu|%_o?LM` z$7r%GJq1C|BsnYd8}!U;tHKnou#7MOsAiu>mnwLMwjr3VF&y)4#zY+?DHc-K*2jUh zhY=&$)7UkgL@T1a&xt?fzA^#$I~Pb5bfOpi>-)*>~DqP z8bF+~TYx6^T5H{^JT0*t8n8B$fS;n64!;tOc4KACYG8hDoUUlU;wppD8bZ1|)>AFW zI{v|=N(D!)(=Rox*^KF`R;#DUzXgnk3{rP8T9zSY%1t~?RgtrzRhlV2&3tShXt0jY zLa0MR&yKpcJC7mDVmIGYfn(20bY)vwT8c3{LmbUBkZIVok>^)CqJmr#d&)!fPI{QM zhM+I1XlC`3e6Rs>taxP$DFpZWX6#d{FD>r9e=j0Ri_%>si97p&9}vi_Xfo$aI#Vd^sN&W-P-hP2}ymJsXjMD(7cCrxj8I% zH7lo53PKT%mP~Kw_i4XXgOxLdl_}OFi{lhUHw)yAF)6VoUMv1~Z2?83Zze0f621;i zEs}HC2stlvxqrPXx@Nn(+qakAP-Ww@RZ*csS5Y?P4v0&?Us=9Ahk8!sZ(eL68;x_! zKe}9!k(mUN9-L8jxYE?O)=PH#U z&@CSHE}UKg%A9Lg$^Kn&-^Ry!x`?KP(*0=4C)Es#De8G7KvLNCIq(U!cvLg0_QKp^ z8tzhv$ANzM67z#y^eTEBZJ#O$Z#;P`sS!;;5853-i-Cz>D>wV~m54RT z{rA3WwtLkyCE4e5E(IFM1@{V3N$ZXmvQCmVu(uaWr6Bb4U`kNiXjGy5`mQ zA3lJ?SKa!d!Rq60)3&@299eQNqt9*j(ntdu$iRW780WKgB_T&c@&GXTDA2kn)R5>B z;9UGbE80_iXGNbpEbZ*qWqud7R(->`D=N?fbH&6m$1>V@LABbe6`%-2!?xIlFcwen<_32qdlAldni!`(r!0Lrw)^o)1{v;#%OG(|_U4ge`lXqo* z`82&~!xUFyXHi$>&!(1>2c93QEjs(Kc-#K7h2{h8b1K^*T*1-KDtr%6_tVoYuHY`M_<~jc~fG zNgJvY1H>#5h1JpXpd^aPvg(P#ZMBoY4^_&EBOCJ_mBmy=Hlm5uX7G$At>2Nmmq|{; zjSt6FKwozPm#lXAs6ReyP@cr0vhVAJQj zAxpL5f@RqqrIh`M(4fGm|TjuFFTq3^0^h+jhK39<1Oc=DU zxkyR=Y`2Fra@|MgjD4^XGwZojG(26S7suh6f!Dl&1A-Q}>vB>IKxS-iHxWYP?A9GpftoyXk}7%}giE(oY&LbA@s^D~E~bfA}7V1IKf3&3}zU z0b51B_4%of=*}0;^gSzEk<7&+!a6?j4}QD7IZt66yKMe>Wb4fcZD#i}xAkw{quU3i z>3jqZEeE+9E=xlXX1a6ay4=S^|HYjcb5mHwB}>_E70u+N9Nrd`DE;JFB0 zrEs|WG^-iJF9`Z*0cJT2 z;+cXCL?5WXZu?OQORnHFl43k4$V~Dttw#{XomG^GW zYP^ytYe=6na2c=|45+msEwGX>G#PpH@V2vEALsO=uVuD*Uiqt;XIosP6jrTyHb)D= z^Hn60hFb!tnTGILKioYpqABFvryv1AZ>T?G!SLrdq(3TBs^Tl114^>Fljn=cqG+1+ zejn&T18B(S`f2S7y=vfN*{D}PN@l-dmh1cjQi>9)7&$20s+Bx%9+tpu;fOzGMcB=^ z=7OGbhihaqNqYg{=&7%`!q{z6-eyA%a(7)CZzo)WH`z4NfmF1&yl2a3q85EjWN)pG zZCq$?dKAD0E1&gR2r069?)r+3)_fmb$w&jiU+}!OWaU>A&W7pLDG)z2|jut{~ zkEj^J<>N+jy6vdaY8d#P78j_xd;P^vFhtN3LK@GXlo1R{W?4VK&h{rWT?3t{`4_?w z4D9hc^-B>#G)~bRy7r|KnUTISGMfe@<^Yc>baAcOuKlQS@1klF4XA&DT1_<(K>ZxB z@P1#k%M23Tatp1t=04y_(vKSSN`F*5LN35LieyyIJ*#gXtw~CP$s!%jqT=va_r0?v zdU1X}LUKg>(!D5RkaA*f$v%q?do5jMnSPLj5zQoF8`#r~sRlDWRKb$FJvf!1a+4b<8Q=LP$~`!!gNvhVn}L8& zaJZf&!Kw5kd=DX``N`I&oBsU8v%JTN(SAAco%*>7`6}s46;Cxa$LlVjW##aCr=)*N z@AMGJ=t-u^8aLl*t;Emudq$NQB}9rje-e7gsbJ{6L2OA_X)`rhpst%PT2~rfC`Y0+ zU2C@>W}7MQ^Km@SYp)9w--3DR!)I|wEYAl8%}*|z`ul;gi$+VkXy-8>QZjjR=NTuX zDz4wiArSRULRwdyuCH=s?;@j_=m&cmT_#MYMFQfp3Z)b1&J+z7Q{0b9sB7CHZQmnQ zsBWQ}H4(l-eNkPxTXOHrArT!yUC#$GW~q~E|Jx{BcYdF`GW3S9BZa0-0+jdE$KOr z`GGVUPRZw;^yM5+W*hAmsVOg(HV*V`nI{K z(dQS1V7iLDoVeWNo6-B5Qfpd;^F-XfW5&Kt8EZib=m%k;t6eFP*uG9{6^I%PRtj2{ zgL`MVu(T3UQu8J%(b+x4qfhFUDcls^L$T;P*+(1=YwF2m)b^f}$>-Rm;yc?F-Cf0% zk5I25fi%tc8$IBoK32%maW_7ym6uZtyf)fyll(Qk+CicdIu)lSb{vh7+6i3HLC_Db zDxr4la6pz$i>y!qVQb#A+9F@cX>cGme0=xUg=`l)>TGPwtj5c7U0OCpdrY2*Wa@4v z0WH(%j|1CmAnrW?%0$7oqdv+_x9}R(%z<=bdurRy7HWSx9Lx{AFK1b@4vFnW!wyDC z#u^W-^M-Iy_ok-?MIC-#i9EoqPJt56(%gFxBeMAD%8x)$PJ2u32|fPToO_IvRBpHg z);uIvef5*=cwlGNhG|2D+Tp#cy8GaLybsCAr+^xt-jY`?k64iO@k58X4nZJ*@fI(z zebD9os*Kl4wpxkk0i^V~+~wS$-O@6@gB|z5w9W{$zAt|A<1R@N1^=({Cz)nN@3N0m z+|7!AjDy)qTLlt0Pb-9xoCyOXk2_>injHtv^$6mia(Z_XIk4Y*hqGn$1~RJ| z4AxKNiQ-8k>sbVKOj?p_Z0Z^dt8HSGw^j#mV?g7zVD70&^>9EB(MHZcAPGe@$8c&x z-W;A{`qD8E{ja$uV`L;b8L<-B;fi`adP#6!4McfM1S&4 zlq$9yiV&duX5Z|T7u(xg<1&P@2qbhcI198S0Hd&UK)$weA>`@*H~{@dfO!9!z>L&% zgH9LomDJ;KvqrD$3bWMKEKa4|wyr~3Rg70GAvF?lMZ9cP6@2*_P-cewYx$eh7wb65 zrp=Y` zOgvr!%Oai33ucD@%Z28R0%I0#8fY<>;-$ev;o&sl1f@Z>6OWv$E0|dwsMnELjw(eB z`Og){@bHceg8>Ti7)ii8{brRxj!)pEy-6Ry#GTT`H~5L*KP;KRG60d-@4YNpq})$~ zK6ofl)#s_ZOxukQQgR-*83UA^&zxn0pW+0wY6i}4c=&)l4!E{ki3ERo&PeTEF?4Km zZ2}sw*+i+AM& z-+(4jh2-f5n+99;loHF3-#JdaR4u1tMuJ)N^M@N3qwzCnSD%T1z46C0eNS=0t^_-h znNe*uvO2bzSh;WPZN&{HJKQUF=`Bg|53%Pa(?2w+Nvj|{pg&DZmWMC6;*!8+k`lfi zdCFi)0EqAe@sl0gT*08Qr7EBWPTz*dENJq*VNvum)EWbH;=(&s)QBp)CRfqFa&;h8 zx5=HeR59^ztj@u5+PSYta|eiOpUdOjdep0_*#GB33m#tNhKtSPHD+^uJ|FSnFF_@h zjsLqvYN;JaHD)b74)&19pvPG~c-19A#J{oa{@gF1fn1=@G*tgrUoG&KFZ^gKsrbLM zGybslg2aQxPzV=W3i@-6!Q8w8e&S7T|6jwFKYy!06Hi|V?0%sB(@zRq@B=)ZcklZ@ zK9YY@{{D|6Q2Md)YCL<%X1x-kyqrK$)SNBe-I~OcnGA4eW_KQ2>|v? z1m%g*0-h$rsoe%7Q%bz?W@AI(BqH(($N%6suv-=%XZ^}6{NW(MmB0-gOEIar$G4Hb zX@q}=b6!iGTzM$M5ihdPdW6-IpabrknYs&BfdH)k$ns<_cLBzbw_TnQzv&=*AgdM~ z5Tg>A^3sBqjGo~yE}slHJubRv6y8du80mFA4ssb#PbA+I{$h9vy2FKc zC#S>+xO$E)v}6Bp*G#6$X5{)EJVqx1gqG$fsZS$9TlNW!xb8VR}B8&3K6^z17~Xs%tHIGTNf{@#4jf*MGV6#b-bH`J4J|S2_!S^oA*- z41wG2m8yNYDouy+$7>|Ub&;c_=$qwAeUn}&C#>=gSagsF9|O1h=D2$@jGd- z=0$Mx@i$i(BW|gw-7|{3`_HQc)bY)KWpi+O?5&!ICmNwYA^->H!L9y5I?j{?!nCQh%{NRb-0Dr3U-nAqA zDgHmc_%F}l58fb87tku?$ep-Mm-nFvZVON13s-)C@r+xESN_~4e}aKOP&D|{1swR= zg?;XWAS?KKLz-xOiOr@W0<^k|_-dUd`!&fwyY|0aRhB2XT8c;k%TNLECn|2JVugbX z3Qo*|xizaerR81ss8S2~kE2jumtd^!2AeHqEa_)!k?v9ABH(}=| zZ#(#!O8OoIa-O?42v|?`=$FiX9ow$1tw9gZ-4(JkeD1&9#_q3gl?gxeLebpKEz#?< z7^{RS8ng^-9*B^HFUo)&)x7vd=wI!qkDv^;e)Vqh3H|id-WDJ535Qq+d_>Zk=DgE#+=;rGl8wREm7)e58FAru^C5Y z0xzvmJly6&jAROU2BIEqwsZf|($MZ*`x1(}YSlTC?2;%9P?grW4J(G(gc6LC2X`1oA3=lXUt|1rdJ9?%aRdffj?(g{>p1#T~!?o=0lurxBFnK3!XtfYKLX`PO>2V`Fd( zwdk!Goi4Kv?7D$yQo_#$SjSZg2zF^ov*g1h-j`U&;~Z&oJ9u*CPFqk!dEt}GPta`DNdi;{k4zbrK_ z-wT25)?^jD>Un!luP>#LU~>}*P8_2-<7M@7%3Gh5Gl=kDwUwRrCpQvp{l;ODe4mpbOf zC94z<@?5i1X}7hKNW!qS8{>61@Ti_2y#gWpAMf|?HW~u&3|>h@bwh|4zuUk}uFrr; zE~$&OJQ)R>%OHD>aYcjbfE$4TBB5$5&t}s7X{}3#7+UhAwZ(17z*v*HQm<)bYHl72 zcI0LB+>r&d<-X@+!T&pvUg4Ls)epjdjhl#ZJ`I z0O@Wow1$q%^EWS_)8rJHTG|p%OH#riAL91mnIhc}s??@Srz||H91oxP*vuFlPO@hT zCnqv<#Kph|CT za*l;}xm`UnNEmy$oT+C1@Uz{^Gpq7xacLPh{>3Z*`?@kjF@s$dL$2IK8UR-K=FTg9 zyted?F$L*YF~^hN1r+=n$`mwzRLf|vRz=t#2tIv~Dg!Tv(?3}-qZaZ^8#lhlaP^HL zI~b%B^w@7~Pv9)$Vp}~HvfQNcS)5^Q_A@A(9uAUt_~X{(4-3*s4yuEw=9BNv$ zyP#y|L!afumQ$4tZ7QM_b&%hpM3rP{(BPY^Oe67~?E-6T!B&*$jS9Cq<8jevX1Vg+ zO3u;>l{G>J)0bz3d;GB2+te;!?if_qzT5Mz5=arQ=vClb2Q&BUDu z8Vv730{?Vd5!5p~H51aDVANe*QT-qa@QWN#$V8pN&1vOIz z@dHO|A3hK(8bxfGHDxyV1j>8_3s%x(F}bHBfyP#KzA>0+y}gAzDOI z?>EQjewtT4Etz#krHwQ5Bs+JJ<8uS?P_WXS#kdjY(wl10d{bg#yQa9^Qtp;RNLB5c zu#dMtkPRH4j1HmN$msg-v{ww#Ak7h8%XN|FM{~$U<1wpqmFtx6KtfsMIbXqMNH%?# z>2#McwFCz=^K>FFO`_9B6*`gf&|&d?_Sh^CBh|i2E^Wb1)#@WhxAw**>InTc2BeZR zkLM`>W4_md^BMne*-1-is zzayw^hnmOuTYYEy4mUZ=y}NA%Ua%v7CDKvZX> zJ@3Q_VhS5-w~(9aP)Mo!M18yM!1b~Hh#^_cPiM^T-mBS`wi^Mt#s=^#nPd;j)|PKR zs!yon`E4Hb>1gh^>qt(p19vsFl6SX@%w%AY(p@u70?wgKD+}?UQa>h5HT~>g2QEe5eoX#Qp zxXEE1R=a*GGuWqWAXt-=Plqyp=!~l1r>_FxbNPhr-pEdOkm%&+7~t6%b5o}4UsVdb zct>4-ZJK2Kdnib0=N3tBfR1DGxc4*oYiYu5j}5*QqdpR_ihFG z@?JNP+7>TtL7mmcVYtOB{*t{Y95&v#M78`a@+9@U)Z zwT(jyktB7a)8-jE*G=e88(TaF!i?#s=_^`LPGfXbu(P9qNI}0I&)6NdR|tF83GraP zy|DqCW+9&wTc7QiwdQe>`o-U-UD3+vaDR~cn_%>Tn3w)i z{`}LAcu*Bb&l;g12KHG9iIWrI`+|b{W~7(_PwWstD^raI_+ts~ zKjTyWH1ft64lySXkq+Xh&wgEG3V~voJcHZL0paxJCi9{FYO{@N_^7VoB|1q7tNjc+ zuPEO`H;k-KEm`Vhj&nakB&wxFKGh^&TmMP@FN(zX|MmKr;g@=#Jkv}t4J#i3af&k4 zDLA~U(}mK<_&S~B#MqoI>aMW_wwo0d*e$VRT&}2Dy-+Sx60Nr$Ybp6l8iIK6aJls! zK7W@;137YyZvH#Bsq$Eq=T^j+^KAcF0JQB;%u;pQzQ-LOn51P$H%i9c#5IDl2S_HG zgH*v>U?Nk~#J1(O@lw6R8qCH61E2OWQVJFq;XvMzgq>;l?8nKrVB+o-&4-^_)3EbK zS5k1EeLkLge#qLX!-vwv$i{cV&@O2lZoqm$EOrhqXA~KxU zVS-4fnu$gXn{>JzH^3YitwZ9+Bd$shue4eaz&uRnpyz5!o7T^F9ZYUfB#n zb!~&J+qO->tB^I~UDfTWj-316=q9R=?Jbvl&Qs^YJ@48W8|-d4(&>HZ;$5nfzw(iP zhfDt(7hK2tgSR{8j#q+LycBFbp>znoMbXJ2p`ns#Mp|A!_TMQ;pqXacp#+a1(Fb5o z3ZF(j=$-2}&dUy(Kj+)cHCZHNfXIXMnzc)PTE~_&mwnDziMs(GvdQ0i6o{ryt65Hg zyI_E?!jeuiP(ER;7beMyrmf|K6 z^aGTGs<~%}ie^C+Id4EAs>b7(8;3^@F@6_^!U14-48tBLqDE6q4w|QSvpDTC;po_f z6&X$y46yE$fi<__6xUGaa*0(DiRf27t`-K6$oCLkduX{M#RZC+G(!Qze zpry53z;5!P&BHmLb@5ccGuLv>)VFd^>F-$El+-;-|IYrdKu$&6d6!8L-RYI#k29*x zv>S7&x_c(EJ1uKFN7TB+H9I7T)PkkGl_v{Q-ceYru^k`2JR|XCTXNNpdnBquD{z}G zPOJJ8St|(Ahg+RPB5T@$d0g#{6MN@HqcMCqht>q((Ex zK0(E@Tv6BlO`FM|IN^;t+0m4`;DDcer2MJ%^6$Cs_s6j#8_1><0g<-;xUQO)hNm6e zfByf!)m8`0QG$x_R|)EcA;s#2x7ar)Nd_+B`6_v-FJR37J<&aNkSrNHsamVvIdDIW zmvZ3*l`-sWw3|)&zJ~U_t7??3u?=n2fCzRKu_K|uyyi@*`69YTNoa41Gyd$*^&%S- z&&=be-Lnqx6GP6g_AO2})6iwEv5!84vW9VIEOv?#dS(t0u#vgs{fNCf8>2{+HC$q4 z+^dZ%zF@tbRie7kKWP(NuafU(`(^#YShY-z<7O;X=EO3X%IGd|orxx)Pga}5*9ujW zOO`&}`wfkecEfgLxTEAqI|P+R^LSOr(2S4{hmq;a8+Tr^Cuj}Gj{|0dyKj`v5^Xw7 zGAqHIu;Th$I4pFS+o&l+z}h!a53=`ESLMPNoxd&Dk^fdYVH~;4?RWE`%N$e$ngj$& zS1wD%F!wU(UAW}0_FRpZus^$?fB{0qtf1|jzyM*)GLJ5ZiCyM6pSxR>pYQJe)%|y} zZP9L~rjd`EKBJ>8MjqQt7f2Z{kiNfsk%MrCwh#lHkd%}_22z) z-FUN?Dx)qQ7q#<#ZF+%Nmf!)z`E8_V_l*mop=PX!x4x5whB7i3sjQ2+N~R}cl8}5Dv3NceT`+WC19>3-?R@6brh># zlj8YZEtblPFINp#9Tu@Kcxn3qLyga9f@Yz{ySRov7RW`#WhdM)N?WvwauC9DH}Phi z<8+mG8^`1_$<|rI~BZVzPn*Q|70Jr3L~Gt=hxGZWF)R zYdYpu64GQ&_1ngF#6y@r-)dFU3=2&-#6Bh^g-Aa#6O8=J%`*sIk&gSk4pt{!=98su z7L@xR)<%xO{34-@dZM!2E4c8oc>PC{i{uneFQQ&EN~zr<3@FAg6-B-%p=cwnK*`%P zhGx=>x%a|7o`9zQK?+gm9u2zc9hurKmED(-^zrOb5&c*`#VXzbX1B|cRT0zKj5K7Z z%=1IvH?b-Y)T-Up3itI{8u(`8KiZbnHW+3zrs0<1tkA9z{LqQpCwOPkOGxPC&^?}F>Q=CnaAybX_ zk88=$hjw>Lv$Oa5jwP7ZR$^b74b;Z`b~WmcA8Fw??4)<^&y1N4uWWZ>_X4Xz(z@#S z_zbsecTSk4F8pAdrM_fjiO{fY zoOx(OzkQ~JyMEV0DyHs3x+;mrij{En1+Pd`MCVP4h5Gfi&F{s~z8Ox0SFbCVLcCx+ zdC_yZZOH{Rce^*m0}VukNBGvQ#2&+tL~j)=KG+DW9G*AK9}5u3+@$<}*n7*M zI=d!qG`PD5x8NS!Erg9*2*EuBcinjK;0}S{?(Xgu+}(mZ8|S<8&dfRQOnp^zew|-W z6%|scJA1FY*IF%CU)|kjBvn=ThnJtNq1Q-pf$r}W`_CW0kF?S3tXWpUW=Sp1`xB58 zdMbUrQ#y^2FV>O|z7j0>rX1a(MnwQigO z1#>g5pCfvOuuc;l<&=Jc-WMfX7J5JYaI7D|#*0EGs5M_2nDDQmXSK)6yDs*@&QB-kMtiM0a-H43V;o6waf671% z&!LjGXbqb;Nl3F1)mf}kvNI6nQ!B#w_*IKxI!k`EI}9kDy%gEq>43G3HR2IGCj5WT zYP47{)?hNLxkM;JTYY{~mLDMXM~xk?|;JAd(0VR0@>ik_b`ud_%_x3CG05<9<8!(E)cX%e()~=lCM0 z^u?@>n@sLKD`kRS32yjy92i9D=V(42suR6vol&EO-&!NaPeVCnCd*XTZkhK&UV@bX z437k#CR`y+8*|eg6a4$?VR86;%0G6W{N&Le$IlR5cg+dGZA!P^Xp;T!y7}YACH6-* zYRash<)j9*Nw5nbb3q@gHp{;lPf&UL(Kp$j9CgRq z9ZD~&4;42=fs_g|+zImTT+#+g3%MiG2FuyjoZAOP-bxryZv($pk*ENU>{3NNO?oE& zh`ZtXzw1D%_`er9m5?X=-_$0sMv8I7)|BY;Kic+l=9(>XDlsK|?v;ON#Z*+T_gJa4 z6;)JV^LJN@962GgP1U=PEKu4>{Q71^Yi-TI{S$=t?0ToG2t5=UX7kNCF5aj7Y1e9r zmY-Kz8YtvflK3}OgZcM#g(b*WlmE{xRq)NG3ck-N9HbOGCHv2(K714fo{pBfv+Tdk zTQ=Q}_)+b8qQ~EU*2riMXP$jLZ<-|GN)UT=)~1&rV;t=(`zG>}oCiycnv5cYXtmY% zYS}7}zW8koApp0(h&H{A`L~zuQa|LioEGZ!lU^)psx{T%-Qp=04Cn z>RfVQZ$45jpaNmUa86-WRb_)NM)IhAvze_3v=C4@9V;80WZOwm|O%E(PriVu5 zAL0EYy#F|V|D3$PSLB~-`;X51$6)+pF#dnDw)k}gqyNPP_{VMePcP*kx8<)*{>SJ0 z$6@{_+W03_`~MiKjmSCYuJfG6QV%F;6_nViQ8rOgn%SsO8m?4d{dxjMkJVWhSvrhp zXiWvzvhh;+iNV&N2{Na-lGO4rcsQdEd1}0qkPGr9zM5Pr^E7?k+@2#E#-Ct2n9lzs z`s(`9%B**jbfdM0YOyv2wwPolP zl>Rj=OyYxc74OqYgRa9)$VpS91ZC3}G9)v+!HS>$^~u=tmN`MYyg6(lqwBK8 zW8Ec+Elq!_pu;Ze^$hq%tCj(CMJAxxQ48eGp!D25H=@RPV0`d$SFrTm{`gPXFoFpz z5J4jvQlrF1LFP+hjrptE04VNV!ixL;Y&i)p83wkMUXwax^0U!vrW}MV^6K+jye!D~ zd7VF{-3ze*f6(B5IUayC-ZXiZVggy~Pn<-Q&vvh4Gp2{1*RQ}6`uS>CwEzf$^#K4r zVz%if28&r*%0_b)z^2vVbHR~6V{d#;Zib95{P}6M^PRl89*@WccUF8dk_~*V5KZAoKNAgJ# zx@~NOYKwOBk1hk-$XyYCb`J8FgCD2gM`n4>W|BibuW{r9f!^}&rtGORGNMe3sJXihkvq(EAX7COkHg5AG?&V zabWnyqxYv`wO*6QMwB91Qz=~_r{5|u!Xi)kwA4W^ruA?g)fKU~=MFUmS6Z4f2V~PT zT`(~ehPUR4jsHU~X`JBJUb`WK;%II$Bb=Zv36*HYhxO%pI@9D1$UE$tLqL6DNwMq3#S*`+JoHlI*kM5qSSayOQ#pK z4z!$+<(^)Z=`=Jn9}j0VKjzC@>>QTVeD*KiIhXF{lU#neGtLB>(72TV(|Pb?AnO%m zMsqplHDhTQ!)t8=-8Ob>ttokSo2764XmtDE9b;>pxRLf4YVpWh6$*UL>;g(Lz_s^ggiL$S1FsY*xiX-I*Uh8J{x~%>2d&hQTM)f6MNK?og7)S9#BqZbp{31Ahbred8$8_& z@9a`rwf#2KV#8TuI-mWA^43da?s8{x9HA z=$F9K%WF)2&vEJ(zoUt6y%XjFRKXM5y|TopcCF*MH_SmsDl-vnuVita`h+&m0>;fV zoE_$Z1LYDrGdQ8JDlzymCW@J#KtT-fDNozvmwVCohWdljm-@8EYgYKT-Yb+&&$n-f)f5IhFX)Me&|KPS=wK{%;SgCfvVo+ z-p$z-$#6~LvN6?{C<2ywo%oBq!a>7s03Ah)ZW%!Sn@*&S!dRhPJcUb_wt{({!Bm-cn*luBy^Rp?XxQe0I+(q-WMiaxxAJd)l`7yb16A+%+sK zLc;Bua4cXq^YD!G<1Bi{u(=8j8}BE-r=o|_Q=fncXtJ)@Wtx^0$0p-=_r=jm{TVjL}*-GPq z>HQ0K5y^47|Ij&-4N}rMAIf5NTdW{ibf?iXMc}@-Kmosu*;q3>r~L;v)oUtVXfmt?aRSm;+t(gRlG-66G?%6 zcoV3=)EPNOjmu`7X6IMX5T7dw2dp&E=f%);b6~Y$ywsCZ_x-#WfX6-FE-W>R#%q}KF<4W!$3{lQ#Mt6ooDTUNrE=7lr+7*tH$27INNcz zd$nxEcwKDO1^9=sXGy+xeh6wsH@MoURF%mUUHVSS*R7nzBLUmoQl@s%Ga1*zC;r=IlW{_ zgrVeVf6SMdx{_VLkK=DLv50EJ0l;s*0-Mkp<+#PMmihB4xG2X z28EXMx&DBn8Mlifs>sehUYwTekLVhqn=8|n7jUA-Vf|!74(}Hq!cq_TKuV$~ELE1G ztG6dIUg_S*xPw$dhJ{Nc^CpYcvK>13AEtE|Yt1Jzar%hYTxIzRT?8c=`fAK4hD2XU zWVdQzphgMwXA+ie*$nEalM4qE;PrO`#H{ZBki7ME3-@`ScjuBy;Y*&|I}l+Z33j3knykb=a=!Cuql^hfIB&p{L?q zjhCr5Q($V|SzBq+-Z|EaDiPc|Z9$Qp$mm~JrxdYlnsp%yhJ6BDr@_ri;E(@HTIk=- z+!QOeiVyvxme)%AS><~-)A%JaT8(3r)Bkit_TrLX!S7{1JsoJJi+Npnr~!Uyq)D9X z!!du;Wf+q>DMSoQULBSoct%q%Cbv}amvJ|U7-jX38sIJUEX{lCkiej{n#pD=%P^1o z>>c|Y68dc!#f$bdtdFOoc-zz-ts0`3Ze=?chuVaMeOuF$TJy>EC`|UZ*?*702j$=g z2Z=bCf)&nOCZ-@L7&l)>NiA$^kzYDRna zfCU1p;6xogIX)-vWhhQ+HWlKc*&~*ek#>BbM>E7}@f_k^Qif;4Oy4Fjk5qE9*Poo( zxhyiF%dCPm{+$Y{YcEcDU(bhh-qF1I{ZJHF+j@jP&u|IM-j`r|HT9i2LhRq1wk!O^fTZG~cNMqkNmY=KDTW{vOrHc!i;pTaqHWpU|& zoCAeT?;||J7m;}PBuH?qg`M}(4btBc*ue!vvMWSCR>UDu&>G9t$@xby(Vc)M+i~&o z5YxZ~a zO}BBs85|A%`*} zvJ6CJCZH1ei)uD;%#B@|vvugGB;-3%v$wM&+%Y*4j9;jjAr06Gto860tl`hU@0KOE zLlx48O+`@pDUh_|*wBZ+JB)_8N`7te@)WF2RHh8m)ccbEm3`*9XAvEG9-Z{vqfn0o z&Gj%7UY>GRsYvjb7MBXAvzLHUy|#e|VJR7$ceS;9e&Lji-FN*fh5|4_(@It-<;e}~ z$>zfbb4;DTVm?hdudmumph`RYIT;H0+VI?ku(Ks?a!69mXNaJ^)nWv+tU`v z$CD+7SN|W{+bb-$^y1;>9Hf{ztIkQ89Yy%Bv`WIJPYT>^bvxtMk7gyv~;Od}v#&=C=>k__?T~=qa8L4f62O<}qFou+X|@BGEI2zG*MGX@ ze@pTihW7m)RzkyPW~JV6VTZ&afb}jFAMce81iofGw0?X!cDU}fs5_5zD^sAg(5VVc zAPm7{6yYCq$n{N|JdRqvBf(gfzqb#N*S;($U2F!ly-5HxQZs#UWaqjq!`8^dIIn0muD9r4U+La&7UYoUep4yaH_^2v> zeXbQlFH*S`u=vuHo2!)eghF=hvy+~1Ii1?ETOk|_7w&_pv)jZN7tfHWl&aaL0@B54 zTzhV|_B7pD(WrKMMqhOO)A%4s+MS-X#-7<%ZqEdpP# zNHr>@piKCj@zeB}doFccb*kK`qLhm_;0~2ML<%jy(7JYdTt&j*vsqyvQ1-UD>adkr zu8>?qbB|>5>O_Vvj_vV5${Ak(Po)rBu&_(zMqVr6HvQUARPN(hqTvFe1-z;<~QGz@tvJ4?r zF+LdJ_8AfN#3<1bOaDagieKb#R5z8sH|Bpd7y;aQ)<+Cay<6QRwwBh__G0;aivTa4 zQ*(Z|oj~>W-M9BU_O|X4I<7hUWr?BTb0dkm2(u`eUi_T0KLx`D_7xWg8t)On5-B5xji-M51^e315{9SH(0Hq?%8kMgtEulN+^glk>@)>$W`qoe@LxZ)PPKv#Ev?x+>+EI(FG+?tzOJ7hwz|ixEWQImrh3I%KBoBN^rOQo3XjMtiMCr} zCpXXi0NUkzh9n~}j>l9TvVeXwJU9*`v-6|tip7fMtV!iCfeGIV*$Ltp7DjUAB&UA; zaI`+MH&*}nyEEvU{aaIG?}r2O#kR*n5`S}A zL79Cx)pz@(*-*$RSZ^^6<>v_1Xh8{sUrp}w1PF5Fwe5`}`tu`O++q!HN%eb75nP5? z-7od6vTxthvrdQza@k+3xkNP}WmU36&U@-TpPU!rB0**(f-WCEMCq}t`*cY2fBb4F zHl9wjGcH(>SZ{3`b5zm6IOGj}cw!WNI#0TJ^n)P=0RcSXaAX8|hhj9-sOSBaIesI& z@U`+mWiKC}9Up%eU#W_5p^~72AZlb2;ur4;$DJe-m9GpWTtKlrC-)6vB(fi z(v6E*P3JhgbFp|B0&*7SZW7;<7j(^I+-tfhCaqH zj&DK2=wwf6Vz_zxnVzRjpOCyZkaJ;}U!Ebk8y=m)lKN_mO4O%7v7p7Oz}+JOSZKx3 z`_Ved`6-4vt@wUQUp~1~aZDW3VpH_I_ZbcA@Oh-nJ_GyKub)urm+P7ZU_#YWrJ(G5 zJBrrya~Aa^PISHwV}Uyq04kzUp648n^3o-Bj$I=OEo@^FmKam zjF&D}Z~C-U+q7t2YN7YC6l&Zhd>l+(U$l~Qc5_6a36Wz-4*X-iP?<1S1J@5UjCH*O zpQ1R8HQh%9S)xqj#?jd-M}`8%T8C)X8K$A_r(hIKqI~VM!t7;#?|!P>6K{exP5~mo z6w>3vNqi<-dwWlFOwzLSCq7D==KSU>}vp@N6pcq;xd5>W zzXVyCzz>h3rPp`wYrF07B-k*N>UbTaqX351?4Sw3a+UFC8bA*HbB13*uycLPUy)Gb zZiCpTXRSMl9Wd+W^0hj>%C?a8J#abFcc7qy-%hv_@Sc{aOO%hL>3T$5AO~iA^$KP@ zg5za_g@vbX;kgi>wS2<9_t>))>EK$Gq)_(%t$201aPD%izq&jla+PSSg3kJ7X5Ut5 zcQ97(L3I81erm6a!a5Ph}9`YBIGkB8zhsuCea4u;@R?D0tIQZi{G7*ETIVztL)_2% zf5_1Y(7FH1{zUrH{~M60Cyn+Q z9jlnr1r49JDtG$fhTFZ)#oflUHg{m3%hW9@Z(K3@8Tw0v{eRTtD-0llgeW&YbGq44 z?|y|APwwz-%-7;t?|`XVo!5VT*XF@;(Sm`0U9}$0=CQBklTGEbn=%Ts<{(FQe>vhI zyP|vi?92B{yWv|XN^AW-!~rhhbE)wF!yM&E5*vM{PsR)fj(^y<+4g!6N%k|BSuOg| z;sA>Z7Cu*Aeb0Y!0hTj8Z4#qCcaiJxc^XW%_QKicto4(Wtb^x4FOUw|L}ipn0y`}6 zlnm~o8(G1&A<(K7nnfd!`Qs^RmZ`nQD;w^q)R)}pJ zfnMda!i|nQZDPdVgKxO?zhSJ^fzuO7>#TL&7ySVm@!>&{d$y4=3W7px_L;U?;*lhx zrY!xt6DeBnTwB(cTvq(+po3vaD{9N>6&T%D6gOfo24&)4Pgw;v_LR$KW*yE4Wr?ny zzwtYbUX=-Xagn1O+j;*sWIbHfp@}M8u2(wAWi;whRPo!GA^6rc5CSi%tEiZK@bj+F)T-zg~fcbKM~@O=#kQMg&Kl zZ3vOPw}b$C8%2!@!u69pWKtW7t$V!-aote-^;5_$LJWX9Iwd!}N;DL}#4{n_`8{EwtSSgmV!Px6>~n6pTTq5lb4Gvt%|rAS3IO5Ph4Y~_Is z0%D|o)?s8GPveN)C)lJics!eE|E3!HE`PFu1q}aK!;1H}L%Yvs5lNyiw|uJNZcf@& zPtdFa$JyD2HQt)&A8vg;@uVn-UluEd@v`5?g0Wsy0o$`TU103(jW|$*$o4k#jB8{- zywGxaca~cCV~}-OnC(M!EjlYLivb%lT8uZy7aFO?Fb!m@eH19}%QL z>!aEy(;rwE9RwidtIe8A&aZQ+%jdD@k;KR!GfFsz4_Q1-uD(CQD59$j49=(07x12h z!6dP^5DsOksjz1#4j=|fCRkCiG**enBls~adHwk#d-Re+DPhHXjkhkX*D{E|j5p+y z;1I*_eHekg-u)AVklt+n%G*DE>5dKpPKs|7$zCWQ3i;~m&eNXGm3IgomA zkcWRIWrN1wcBm&od|+B>oz{BEErX>e)DD)} zFA`-_IS-`AfkC29p}tsu5{+HldHFy`!mKs8m-)m z%6ixU?^ay^S|na%A>srT%a@27`|M)ntY7gAar!SWkCoW=7FhhfSaI)G**??Mo{DBZ z0Rt9FYrTWw`lBg3kaYAn90HC1Jl}k1^O#LU#(QdMeK3BjsQ4SdMG|%Rj~J>cV6k2y z>!OM;ciY`MnSa*&>$BrA9$*X+br6pI(#7NxxJxt*-XNqRkU!0ti+6mTBvEa4((_YB z9VfxGUm#FcP?;P}A;yv8a34W78%zB}p(uI46;gjpMdw2m|9D2uMqd>qHg40x(jn&c zD@TVPG2|N^nPaU3SgiBn>s%?12)OHv@=L%knSp1>I?aR#aq3_v8el7Isy0fm{9VL; zLqYJ*gDc;#5(wCV4Pu0C*W~BUJg)ClK1Ax6K1WFFHhwzrb%V>h+*pIb!s}*U4r~>^ z8b!@~lKge`48f4?k63e<0|pXGC$io!b4q$aS7Af@4_<7yQMFP@m#f;~V=8Sx>?$tx zwCm#4LeU#zx)S6OV85?>84E?}W8#TFIjw_pWNnm7LGR2YzjP1E!b4fBA!NW)|38FFWR zt|IwnwIPhHQ}*&S%|*AlPbv9Uu1Hl6it*CIwZF&f%>WBu{)Tq|r?J4q11vE1x5Vl6 z*gfh^0Tp7|Tm@v501=T>?NHsMx-1+IpEGS@{3hFk~-o!G2Z zpYycFuCNYv3t>^FtWC?0_dg##+tOi@~|dGEs~2!1TP;ML&tcRSvmyj`T|4;YT=kc0rkrVPaxx zfcGqqcA3&S(Mk9jetIg@)Mw5;Lk-2PUOAlq=vptqmKS)}4nzkFRQYW8YRd2+1pI@l z&lNBzo@488Em_`V&&(L34S&f@3-`#6gT*fs1oLRHv>tRy2nH%6GT*LE_bn%(-;50A z+_Vi0@EGq`#9(}Q`Bfi0Xlyc{?*N8U(t_f9KMS_VHS+HauZ55oz2hFnuNn{TgUEQw ze?l4cNaL`5A9uuxn4~OGIv(D`LWzXGhS0H~9Hw_tmC;{C{;;8)DJ{u9AASxLqfm&s z130-Hrp8OZ|K%c1od&tn2HdB(`2isE$Pmp?*)u;*8`H2LAw5IYrC5Kd)O$vp{2hUz zE!XdqkyK?j%`k7_TgTF?9-w#q3}1L-$oXp5Q#ANPD4CW0&(V;YIh=9!lJ$Jilj%_O zCv-Al%99q)5j9k|cE5PmRHd8K-&Jv{&x={#RN$fCGd~zzyd8hnVkk4LFd+v z_2bgz%Clb0-ER=Vh@8~ga+OMnyJ_Tsh?Um`YAs|w^)9};S|0;5m-3VW{6k22+l-c7 zY>_8~ih{oA=7~M?QZ3)$Yv;^vq8o^JLp3yPWzf9R-W&h{R4p*@LF@+f|$2 zGZpkxfVOq@DOya%lE*Jrmg|?LH1^~MjxatY2H^=&G1+;7&mo5-0u;HmNs?aY&!Hf% z-0dV0Qm?&tPa>6Hd+t{(^{z0HvI9Q@xjPo#0xZW*!*s^2rX0UnL(C{Mqz%0LxhuZs zV1_9itt~OmhgQKrgh(+Ty&mq`<|cqlgqfBI*dT%}eoX3sJHE2@=ApIN){kl>9X8cC zeNB5M9i^%;PGA-fdVp_qwsGHxwGPFeg0CM;8X3|kvVnRnHBmJ8jI;8gV{)_^fChgRM|9rw*Q~$iEtLiseO<0uFy}igiohQ`T{fz z_BZ$Ya`6jN14;UG8YqLeh_&=3<*)CIO_u9XCK4@FhZd^FG(s^+&su9J>{Sl_1NxHf zYyVA-tgP1gH=~b|uH+8~np#*kVhu7H_ho{GiqIe|)LYXTnd{jMqz-*0V)gB0`O`}i&n#~|-+ zX_#=c&+O6`tpntlX8f-kLZ3f-AC=|OF2ovJ?ZNJ5jefTX;g~C1E&v%Fbo%oR6ogNBM+6)yf%Ms<1GG8+%HC?JT6 zB&B=gTT9NSYDCVmiiy-~J4A%lM{ET0-(20_Bmts<=~^p7*|srpO9+$^eXlS?hZ>WF zg112FwIC2FKSy=GJVB#Kz%#j<0?~!S1Ce$G6E6iF&(0hg`KV3cvP+ z{T!wR9HI(kc${5$=gukwMUVhHhOzgx<{?|W{nvH)EmA~TC$aP}%N5x`u8Iwzr3;ML zAUU1q2Wl3mcc*m8!OlZ;h|G4vmXwtJWa%ePV#=1}#L4E$@E8)zA&ON#^W|6S2V$gl zBPpDHkKcu@0xq2e?0%rV#a(+OXk*)Z{_vw+mI_t8B@Dj}WYfl-*|~{AyAPdEkMfSu z!8l#el-z36w`ny-(Kd?zERS zfs#}t!|KG?Rn`A%(WK@2=S*RpeV&7vKQ5W#aGRt3KIruGi6xu`5O*Y@y+`gTaJ-CN zpl>o_B!XGzB0Lousf%G;(^uU# zb$%zYePT}SzB>3IS?zH=`|`A>eH}ZzA}Q%x_6oEX&p5CjIllx_7q>Q~n1kyq4uRGYegb8SRe!V+ zq}mm8SVJeomJPgH5eesS{2g>t_YH}x9^8jc=VEX7ihyKy;dqt|{RgL_{q1r;W0v(w zHM0AKM}NXoDIg7gT0w%?{VX8%}BIuKIy} z%m;uslQYAnxnfTC`w*OWSVEyqG*HgK27?Yr-xRDn-sH(wJ;^rjo1AjXeep*D zDJ@A(5EDKgsOmE`tSgIcOyyAPK%v}f&y=*KzOwFdpV>wyt`5)oUvp_3) zV8V-chFRiwlb-YjPh3#ll`Fz9Pht(5E4r_@uy3fV2RbH02z-o*{24+b4ilO3``KV~ z&2nkzhQ>G)&sc0GzOxjs(X-WFhJ<2a4ok3GMgiMEa4i$O+K1U8x%g>xC`;$;<&-bL72y4CawS~aTrNRM6JXr#{ai;Y)b$^6VKpDTx zP;#40wEg>onGn@NHuFl{r?RD-`haEii!FXQ+DfOV+hx|e#)2MP*c6n57yK6$s@?nj zHdbCc7IwKxxGfSJMT*F;lUw~#2>GgsA>H0jb_B1_3A8oga(2gaWw!W;<(QwpN+I!_ z)V`qvXk-{PSOtbLK1=T_l$SEqCMR7)9;;ss*)wS*5h?gy}}_S;W~cc2}ET zG5Mh`8DsFp&y)*G)DauH&UH;-4yjkX)a{mj=iqe6wN2R*y9dhG>EYDMmg>aW$^iG2 zu>m)|<5QWT$Zksi56kj&iespKB&!15>GM)PPZw>uTH`&^aU~qOdikwk6N2=OClvhJ znv99f(JJ>V@tdp1GtGuxXqN)|M#(M~DTIE3exdXsiWjlRXeC5MWir)ZkNOr%Us?f9 zY4UtK*hldY7)=f?GO$y%QYd$0a_3Dy8K!|mNYOSBJ?ps{CXW6*Vp&XHp%M&~Nq%(x z)vGdF+%=$B*=34MMFy_5G76Pk?-&ap?RgSfaqnYg28;edx>sXORZ20w{*KS zu_a=0wA#3;GETeKp?-vrYf4D5eCx44wzg9P!rPu%6h%uH0!bfv`w9tnCO%tn9kVEd ze*r^kw^;&((Y(QnlJHD=0>$>Dd+wNY(!d=slmsI}?7=ZgTFQ-i2Z z&3v5UBA)fTKY<{gEh`P4En_JO833Y>Rk(?flaEu;^XJ0&Q?jY0-60?`gK4BziZB z>)wfU=yxc46f_UE2>g|fvegcvh20m^K~+&H#0l1Yv-g2)RN5foJHPPKmq_Ycm&FRS zc-hDEs?@`6NDx~*63Tp$PtOn7@b2a(^(e(U*xdw7!0r3i1S`DU+PZQU+$4%_#vndj z*WF4c;!&Ev;;21E&}`Nu96u~RM8c(9kGVf%UnC{{S0(a+fV}NMZ?|{AWgiQK?$I4! zi;aq;K*NDa9)-fz`LXc2+~H0c&G>$L%8YB#3im&KC7sikmGKI>#~T-Zp`wMxZz&x7 z!C?eA!q(a8Sz0{DIkxzZ@iCqS&%&zv<=I_9$u~{U9>JBE5LHruIb_G&cmWo^;?>Zb z(6gD=r$*TsWloN&CC*rTN6Rm36KU^Ziv-WR(u9fbVK+4m6+_r-Bv5T8&vYenvj^9~ ztLwWlhsg0OQf$73*x2&!I05v&GzUnyTpE?+UcC<-!!&B*P7_; zH|HJMd{MAKWC!d7qt|C?Jfz#(u(*)`%0z}qx_Kv{++QYxQIR~a^YL~4)g~^I6x{LA zPt_K(a#iPy$|nGZ{Dh2 zgBUMlF*|(34hO+&{FdUUYQom`e&C@_^g@+_XJKA%eAEeplR#bcq0r2T>csGOZfTrn^6cT=^-##lc_$1kk-Ye0S#!n{LU@d=_8KUymTbhsZH zdn9;F`37)T{jq}uMjwORef*j5SE`&0T8&!|JD`1 z-+?ViB?yW3qtmr_ab%}-{&szM>*3lvQeqFij~#+mS@JqNSn6LVN0lsJ66riV zMzR-mZEnd$Sh+K=TF+7Fj%}d#vdQo*>xd(slU*8Up4RmI$8^~1&k(R?t@|#Ag!3vA zQXBL$P5fH#(~M20(a#zZWt^=U`&i?-s>u;dt4`hfA7jY|zW3mC3&E3QZJ^?P`y1k5 zb#Y4~1j5lTnz4E216^IHv(j;d9z^%6E+TPNVu;<@3H%gfump)8jchZHi8Pw1|N45v zw_q7&^KAXs1$MkhdLDNSIE)df$S|QVg5F>M{b~bdSoD_&1AcSNoU**4L41K_}5LLferT zP?+CU@dw50%wT;qd80zv`&}TYed_ZDh1rKBHnU;>^){3^MJZ_)tb%7D#eF@s-Zxn= zuo#;A4$@TlXmIQ)b4FsB#~2(>}Koi##{Q^*P6Q=!% z>#VxREo`@pRCI_bS1YsjJ1SnYFvj6gn3Xo_Ff2CJ3P5dLCQg?gu zEXXH518pCU9?vXU#L5Mfhjgs?bZC0*D5=+iAF)!{4a*!X#ofz|2NX-ZvR3Ac)Xd8q zWchej0yd~}dvuh!e?}ai_-$jQM>#G0R6XwIWo{i$Uwnu3D#q``$jCW@kkZZb*rX7X zp?hq;x9T+hdjM*^SR)uwCmXY(Lr?21Ppf#o;%fBW+1cz_Ef?M}PPTm3!_**26w{Pt z#aP_Iv@p6lroExcI+@+8L`r>~C1enwo9ylkp-i~!kC*ry`Nm=C#5yH+$LtIvW(YVd z4ktkpt3XMo!@by{7>^1nd7uDhti3#JysTQ4x^HU`f_%5(HD$F{^KsFxy~rIA-k-)4 z^!}BDvVnpX3)0dGsS&pfH@F}{ic;`#RrNPDM=OI?qm@)cwv*tAV@C;kpo{le&YnAm zewkjI43I0#0?x`hArOp}A=rFd0y9Pb3ucW6(>GPy2Jn0Jh3OVMQSp^()eP)6&o6Iz zknom&r#dHTsPha{sOA8Y=8`eKwBqr{QlVTbWxvgfgnAuOyL&}f#PfJS4= zJXQ!p0vfa%Iju~R1MV0fD^OBv6bbZAX=l)VofmQ6(5;~5)3FkmVWLjRd~203`>=J; z?4UVRG8_3>S-OZ57M5A`+WRxSqVB2Mt>qU80*%tG{5N7X9jrs&hel(CqAyEVCkl=# zwicP_-ao&e`k1HxGKsoDFrI%0x(oFExZR@?ZrYvgd`KKIBwcPr1ZTQBj5}g(t@`+V zvwB}YiW(CdW$Lrgdl;i=hbT||w_D%$D!_t;V-mt298Q2J@1_1|Wk<2YKXe{`JnlfQ z)#W8?r&I6nk5Ne22EGx&H=6l(AqE?taM63j+}Yva`%w0jjhKB56Ma%I3@!N`Z$*n< zhdxgp?$wqL$cjh75NSpm-m^7n7ydB35jGPO1CnVb5LGN-AimIu@#`GPN zS;wuSZDj*&#H?Y4!^&{Rixx2|7&X{{Dqjx6ex~RB;n6(eGJQdSS+D$Tb}+S~w$($X zxW85&us2kj*>=_cA8@4&V$jO|RXpP7<7_J4{t)-EB>Hr@pbiiHSnp(L?Uq7BNW^Il z{>Z}pnk_dxw0)Jd^j?#_PrwF}B9CK|?d?6Gzd?i{DZ`MggJZwnmZ3q|(N^5)+5F>gQQ6k5!6zoiS^n-yBgvIlySVhBF%{CK^ z48MN)x<@?0m9z@gyW+D@`Iu`mq;oi*@7s6!fOF_xrOO@+2QD@2@g`uFKd3|@P30wL zO5<_wFnQBJ+_;wPht0)h4pgNX0^3Cf1%;}ED*;F~d%|jW`FNn^@l^a15lKOIwitg` zjs4kL2fYj;X})D}T%Qkk6u9P~G=`P+_5b1m@aXXpw{-U4cKsqj(a9fO0CHn1u9*!w z^$d*u=R0i>{D+Wc=w15c!mpd%x3L^KwnN`6n}vNuCN~Xh8O;M4uf|Ifa4@pP17e`< zaj2qy(KXelWM|h}sL39hkkFkB-0j4D0qGbd9{7Jn+v4`Q0I*b(K}_4z6VtNM*6q6l zCQqG+(LY8whX>UGx87nj@KuEPVO0hZzZH#K^w`>*8s1y(-D`goUUt?mO^^8do>ZPg z+>aOXljwoYm_qmAsocTCFVA!{GD#qHlSHdP1B`BAA1DYJ-_Rd=wRf4Lo5ez3vRBEZ?U)@}+2gnI7z z#6$&6d52uE4^R@gY%=O>ZlDJvI*~hD{)O5=`Y_fon`nVS8|<#-Kw@TKIbk^?G9KtJ zod)?SqLwvEok2?Rf2%O>;eM7}>t zpJ}yuaShz2!!ZdprTxH(>y1@AmWG5B)s82A655;(w2E?OFb-h*+rp6*eZEnc5$NTV z=HZeKYn1d?@ig^~=rK2wiU3WYg5*?^%yx4Z)5t8rUXm6s<|pbz9Izx`HfFM6!fV>z zF<0sQQ075I7Z;E(@MR{N8kWPLsmtP)Ss9z(#cCX+7?pk^@3_lccMw$2BWD44!Ks?J}%Hf!4dKngIH^ zxV84M|1zG!q5HN8D5^Qk0SNKM?w(lguQ7*1&huwytAmn-&@k`cu%+2slL+iJ@lZJR zbgUCKi#@NDyI8nqSM6)Z-*SXafu9SQYd~SM&CebO03)l86#tb&@pbX&dCq0EB@D@J zXE;%uqQ3j?nAbg}j{?Zy>3p0QOf`n@#z9mbZPZcxSN!(|SyMf5yX?-f;OQ(!b}p*| zATj7_3II_c#qU66H3V2o%<`1Yd^HsZ)1mb7HWy8ST2cNg$zLS7Qk_QR6E+bs$`$uw z+1aw;uh9+d8Me-NTWW~T6z!w&B6Tz${e>w7FR0T%7h8Z6gq|KI-!(ACyN|^OwW1>oFOM+sYdy_xvqk^C`qr1DTwJA9$eYNp%x16- zj7#+=`hJD-g2G~f)8(sWAQKo~lBYIQj?}gpO?H)-rmvRu1A5<(2Qs<##dp8VHdblg znXu8}AHteOR@v#krAQ92Q zwg4fZ#%iRCHo1_Ve-klXnNj1dl{10_CnCyI->w~_e+ZJ%J5oO>CHmJ8&S(Kf4Myfn z??Y!0iZg2Ja>_j061(vSOK}?*xHV&i^hQPg|HIx}hGn^Zd!v$qGzdr|4N@W@-61F? z-O}A54T1=$Al)Dx(%mQ`-SyDjDc$hSXN7CwKJVFkU)T9|{$Cbe>$&e4BY$JeF~=?c zip(9JNY_{ECHNqH`CL52S5X1e72#fF*l@4Y03E}i=M(M0Pkt$0=PB`vKr*HXS7pbu z9Z}CG`yBh(-m=qU;Zk$$^15afY<>6Ctj)Sd_Lja0N;XWYuApc1lEi($dsA4E+y5 zM{Fc;&@Bv01zbUJaCoWMde*M0r2?7IA>7`#1W`VgamS!17dj`6tXju9wma(+m8FLd z4$}4J@dh#!pSfl_iCZ`@605w{|E%1L{Dy>)@Ey(yg#g}ZNxuySk`Ti;?R62}pX&BT zgNI(GlL1X9U4w#^k6@7Ck$ARtsFDPd(!`{A1l$*Na$h^lQ)#m%S;^0!8uenT*-GRR zlGJu5UXj0k98k)Mc~@9VKXeJ6qRe}misdN5z)E91x_N(nv61EET=sfIG9!E{&B&fR z=#TgcNm=i+I^V2GbhqYC4JWijXPFtkiW#1mrC2X?jr-b!fak#F$R;oou}>|v6%Q67 zm0rh>&O#S;Y#Q2uw~_FEDeM~y=f1EF&sBz!k|8NA_g>Jt>5^j!k5V=T*M8ho>`0Rw zNbNL>AvHM~cEqYQ-W)#MTonS>4}HbBrAVsr>|Vsb;vwX=V~}Pp0&qeg!b^GKDanHy zxfvIe@J6BMahyF-;VYd=&jcJ8^y~~i_`CJo#m0?0sG4rTNNT;xzQlo3UKjCCNK1oz zC)XaWebc!@a@Vk{*E`eTd#P8KxA`e1Bd3Q`WYl}2d&`knMI0qw&ha?z;Yq(V-#zX{ zu@$(TqKD22m&O@5>G(7$+pj0dOE5c!PJC>O_eXe64!BU~iI)?uS^o^3kQi$#^X=9o z_|CRAc?cDV8F}W0UrjFxHgG90&aIN#JS7}Y@^}*!nyYA3Ox@_~*PX9!g`kf}V#{g{ z+0oowHO0hDEy)T*4N=ClAx~tyw!rii6aS*J9sQ1UpRH~_AdyYq|D-V|6p&>+Dfs;G z43vGJ2;iJO>N`ALe5pK6KmyKe?_wlNG0t-}GrYXZMIKpp_ES`@qgVbd{tCw$;6rr0 zOs@j$Y6UOqyva7{ed(F)?V0L5pK{a3Dp&Uw0u<8YH3TPzeIQ|gDX zzb*A9Daa2>-h~&XbIn+M&$7{}?c$mkhqWv!S)qzN9=&R_M*#5 z+Eu1K1MC-I$?kQkuK$DxcyQ{sbczYj$c;MpB*cW{>!My8J$68i7hEJkT0wG0F07i# zi(gZUDR)fw)MY~TGlhMlTrHm3ZA`cL=u=>t=(~@m6B$2MI*1TS$(j;7NUI$2rNZrqVxq>u zJf>ek2cKMe%l75R4kzfkXl*@SRt#+;iN|bB`~mp?Z%BI%Agz)E(kcprk)BU%Dm)H9 zt|CyUU4BGZZW~FCS?T!_9geSg*2w5qvTM1V^`^S4Wu(+F=>)<2MAAa)hz)Xn*$6T?ee2vuNCmWqz*n#P5uY(Y%@xao4FuPA45hCI~2VwsEX* z=%s3>Gl12BH;$H`u8H^-_+e*I6wvv z5Vh#a{emZRc~62ghKOWtY<(zqqit9)Q z6`{KI=QiMM@S8# z4b^wdystuBWXmD)$%ww;uP-oC%?}lK>9PoYNk3+f%#5vI3DkSG5_7ZWV#G5mKMj4-oYY?x}w+;=Hsy&q$2`jnAL2duLe{aEtXyKX%Y zCG&Xnh10!jj&JmH{^#dr_9c#2&aPV#^rLOnDd12nYajb81X~v#@r5RX*t5WMa z$IIb_qD$^4T`s#7ijWBODZkA|b`f$`2a9BMPM7nm#6NR2jy2$PlRzq>_2jIVQD(T# zg@j(U@Ut8lTo6AV%k^a2<7|nK6#L)qdL=XUC8Fe(E85#qK2tK>H=R7jbKRg{ByHNv zVF1at3gWyRq|8D>j{VyH`;4(1CBu=BE8Gv=ZzYGEXk&#W9U7IE`)nK0h>m5xs4RLY zqLwIy5RRF+$eD*Pb$HpZAcYzpGNiCP*Vf||y$PTeA<{yB{456sw!B_aWahYZ_ zo+rs--{cf5G#M))c^uEVUDqC%Jym}@h;hL4dOeey&Hb3>Xj7vixIGBR1P6hBa=6bn z?rXDc^}Ldj<4Si;OR0q@qXM~@z!Q_oXA4Vju%$7Bg^=F*9Q1X^6;Mltpgvrlo?+3F zvn75*cgOdKe&mGkm`vWh2~s;b)%UY_cKTOVCM-ekwx7?#!%Je4uB%mAW;?Fwfs8S6 zfCz5L+-u_0IACKMV_842F`>-}z5I>yf~r5Nn!M^m4xTtOaZznv21I!mKg`{QGhPOS+ zq3NS>w#XriS1=_Q+Wd#8T@gx`?*?gK7ru^FHWB3D^6N zdH&3SUOQi|jAZVKV%v(kSgO6$siHzc=0nnx=b7azBdFpM9rfaFm{lNQtsw!1vixrhIGF$w{No;y|mZI*DNG9`oYX-OOuJ}-8PT6(^svyy)KEAEL}#0FmOpl;A-0xkM4e#rHEQ=6F3Wnwel*~29If2Pk9Rli8j@=5qtMkvU0?W8BOn9=nOE9!( zPoiZW<4gGEAVJi7R^N5ZqZo01gD~?1JUwc7g@QoA$hEqz=a4=B3%}wdUEC%j6VqUhe%zE1FrGT`V!3Uwyj@UZ&ThbXty6ej0;Ad7>5x z>BXkI%VDmBQ**4bTedfdf?{tA`xTa33x-_a9xb@Ctg1VL6B&n0t(e^?V!yl}2oKB6 zI2Ov$P4|9pB|$mE>uSLqr@CKHLHjOVLtBW|t004)7pl5R32K#X)or0Rsah}JR2YDJ zDCQ+HcN2rjNBd>(*;@F@glSiU8estfkdUsBWAzQ0`KkXt_-FoyA70=r@5O@)9#*`0 z_H;_M1iz0i#I&DskW&1Jw2~dmZFDtAy{9gg(TlByjNixwae`w@N6uni-1*%xml<`s zStK;Pw(jC8yrItDzx58?xC+e#i76R>Mf{3OOX2?5q zmL=?`Q0iH?J>I*ba50Az<0$2P8?5ZYi(%-EPo5k;&q=-tD8kc&(05LPN?a&A>gdff+!F+`?)K5kRC_m z<24&1R+|y?`mL$;q(^h;Z!L~y+>2uPT=|x#PqulPBWQ0P>Uv+*>5-9uhDef#sHUcm zo!E-eh6zS!L z3ur#+9a~T)lT0q(1YDndQIkMlc1G-a`-9HEE$UO1S2_?FT828P=2nX4wuO+`R2?Ia z_5#l>{PW&kuC}RZ6J(K4D2}`C^@BTWO7}M`u@fd0l-^a@jX=sCb16DuzdRyIda;`M{Md{G%}w%5F9m%vts? zii(B3QR0=m{1l7$g%x7FV>7zQ5r1Z`uggP_zadxpsUdh4*P%s0bGnUFV;B0CTXJvM z=y|%aEp6l9jLE!P_kXP4D}8480fPSEa$>k%{oYC^gQhcyQrqbJcuXDWr3A8h<$~3B zXMrdTQ)=>^Q4Dl+CXnJc+2yLAZAAg5BNf4Y00w3`$@abxTjb3Ey$1xeuV*DA41c8P z5P5o-@?cqwyUK&Ai&hgsq_#N-w5L&QUG(-N z2)rM7V1e0<+Zmz!(S=dTL^efYZa5RJ*ASC2gI&bw4=XzX+T6XdvhfPpG(04FK`n{b zSCxn5E5?d>hussDyi9I8d5QG0szMuQtuW6rVv-758k(THkVVRf!4sv=BPoHLR8|72 zfA9lA1bgFXXa_$tnd@>LMbYPX?INeYtww>iNGpncu$ZtS&%5z4ZOp5Z^eDKq9c);W zYpp=|NB1~LF@k5#83Jyyb33i%GrU7mpik`Ha(LYqQ1u9gEOENIBaR#K;cNG$^k@4Q zS=s_*-^MWb-F;Bisc7)E-@=x*}-`U*)6nZ>kZ3_-ERlT!PjAsQku?t zHv+v(x*6!1H=r~i(({m0?E%@7)|UZK$MkcYSiqM#o8Kr6 zWu+TUa)M4xx+^URli|{*Ph574srlZFt@S_Lb-XN#W&o45im&4;Zid55x z88Zx02}H2BL+;=QfktqJvH0}i{HDE*g419Z5jaRs+)fG;s?~OIC!c~0+bTK@1Dm#9 zv2GHNc;@rCOW4PdwSg}Ph<3;Iuja45WAIZvs9q3~N{5>&!9;uf?PS(Qx%X+cz`S)j zoGeQF5!E^b*EW!+vK>tyr`I&Ciy6B3HdkVj zQ)Fk$CgCzR8)(IH|DfP&W!5ri#=^Cl^`aW!Fe_K?L|^qo%92ge-|$!?>V3zJxBv?; zhHg7-j}1Da{KV?)NxAtnYcKbdWh(fqtmJ&NI!>Doas~ei5&z9v-{W1O*Q>_h6i<@- z``Fuv^Ha=2A+^wpSOVKvLiU{0rLGw6r9Ip&H+GqmS2I=v3lht=0roFraSmgCK5wg* zZ<8Qbz~X^Dc@mi8sFWiw&wWpSYB8DAJa$LWt%=W-O&YWU7&$6)*?mm5M_c*LdgJf&Fj*&QntwWf|4qH?6MFm#Xd5KZ?+sy@b{j?o6U%^k2n(3q zVmo6&N$Hf_pYq3{|yUJxO)qw)~?0*{hb9s9cnXM35K7&QOrSco$WT z8S`=+u1~W1#B!=y*LhcfhR&eMt6|$FFXj=$NgW*;5BQD)@qhCjhimo5LGQwDt&NKw z4MsKDt2tjPUcLEx`_*sJMDJ(jt~a&lQfzpi*eoia#f ztMDcP8x|!OQ*CTor+e}A5r#pqHSEWncbii*NrbLVHGWomrQ26w?<31(ihat4G`I7p z=&2D`A5>#8RYfrzxG*9KobzkWW?&!!-TqqrYgGAR4Z7K?CC1IM=?#WzOB20g9_ z?<*XF*!_m&d?xCmy?VYum~7Oi#<8z>j;UNM!5v=N0}z9MoxymC8qfzVR6zFKdKFCe zGfps@Wk|Wp3l-@JVtv27M7Iw8OYh?z<#*>sO9SUd-@)OsmTZDGPUPA*U7@kjyorwj z%Z<+!bc^?_y+}iH+RX&~DtbU3k0NC!ROQ6(u=(swke;ol%eiG91M&(>-n4U4hMaq5 zU&Yf>iC%TbA1I>y!VklU-c0{q~jLOmcJ{^uvLK0 zZgsE}2hg0-o0sUs4&mJms#NWRnaMTypao6)8iM6T<-{4~SXxu<^=x7h-W(i43-m%+ zB_7T94q2+8;C}0;yLx*hvw4JviApug$hyPyVCm16O6o9n(APoHNB8Q#R+?lII% z-1ns9<;9e`NrMUG`G)9MY?HHnxF6kpO@SyY-0WqA?i$O%n-O+h~> z#_Pq<;tqCGV(_haEk^r4h=@-}x-bkT zX_?m+Ho6I}wik>&$(nx)Ec#PvuIY1{N*OMt3{Q4;+x5&Mo`?QS+UG~BrCsMd4~HkJ zSNe3RRke>K1`A(k)`pUP%WNw)!{pqPKJv&s_fQHw=z~n>saV^^5l;2*+63`Vzs6-4 z)OK=oN$QM2JavRLIk!@{eX^p*z4R%#2$I|A?Ru1GVvu6)$#XoS z+LV0%&WjPr6s9X4oJl*@QGukC3PpaCpp)Jele|Yo9@;amtMANX(jKx;|Bw-tDXLdo z-;D{_nkRl3XP*2D8FwAxfR?%5Yd)e)h&P%N1MU>eyD*4|=L#Tv@Jf;o8(227TSviU zke&^WHbVk`;eu$8$T68=M8roRU-q#cO>F#ZaBqLHN~~e^?qK&dx0wsMm17&iyDk{d z+R;-XM)evU8rh`vtyXMaW5!XnS$V;V8)kdM+3hcfH;X^p7E+O>A1;h&%EdqglUQ5? z1V!1|G3yTL9;0cm?FfQJ#k*~Nav9H@{Ml`K# zG<;>#(Wa|>X;dHb#nbxf#ivMJ`1*Sw8@p7AzK)2Bjh|7crhU_2wOubGx{B^PJfIhSsv-eZup-6dvPlB;~T)i*$r zElbufdCg|3y1JRabLtE^xWUJWT*pep&BTdyW+%z*$)U3v zDHjUjo8dOQeFYO@a_@;I)jSs8nZ{mOadr1m<(&32zMEBV@5v5B*}=ZNbCy9}*USYn zl;kdE8+AShxX_wU^;jZsl zolCffk`@>?UDZD2h5Hd=FO%LGrGBWi}Ndi zzKg{s%8dadH-Gi5LHYLqYXUd*`Y|ZeTpkY1Ugpl3xAsVU<+5y?*`L8;< zWGsek9!;(uF)KUI$()Q!RlE%IlKn_{q@ws>@a!T+m_HHIFbe`YOq_1V4sy%RQS1fg zwAdZKR+zSXE^1KwU2h?(_k_i2_lLvIr~nDh6~Qx?-2sDQ;~otyt7?suT%Cpq(1CT5 zdv@~3!gcu(u+mc=(=G>3qDCeB*v?%ePF*5vCntW+U%W@ub&5&uVs_{7$~Vv#VjbZF z@;*qoetu*TtHG#I-kY0rjhfl!xB*f36le>TJ52(fwYSjnVZ&H1T~{*}Lnrp9oh)Fg z*)qdn)bFOI`P_eFM(*R|0_z`KU*^age*MLR|6I(E0m#-x^ou-rmO%nd?^nZI+efiE z#vh5KLJKshw{4Y%9=!cQU21!#Vd6s85Wmb7$-Es;O>z>oaczO}K8fr49#^E4S32M7 zgYuQL4wI5>LebeooyM&GvmdIV#G6xv>WX*hly59`bW5`2qw^U*g_p9KK`3T(g|F)K zyww%YNSg|o>}Ql9{Sk<8hc6riSy(paAeTqgH3%{)yj2iwaduYKvq6(Oz3&pVTWOL# zMh@$bz0bSNx2O1u#;16v8($TgI9UuAIcSKYr8rLKiB~)fb+oECjoF6ipqZ^b$w=*2 z=^>O^t?U^*uT5<$zN)t@a3~KXg+#p;(*WJi4+sXD_(uEGr(yENBLW61Jl`{H&MIlp z$f)8;`JLDFLPlHUO|mQsavKok@tAa1iPdXMGM;Zhy6hT5Gc_?tAYJiPy+b`G(Y4na zYsw@W_KCbH31Roe>W$X!tT(k)I&mX}2!7GDlG{;{FMj5AeRfjRP(bv)5n1DGkjtXt zg`HilgQaS=Y)_u2A7>Ks`X0SZ^!=wcjhhYZQu5P8cIzK-132B50!gXUthMVZe>xUu z#JC=)bOxYhh8V5(Dtk-zpKlC2C1kcEVny~VgE$OhPQO9;l0;d|>u9QIHD@GqGA#eT zQ6ZOC(QKOIdF1Q$U8f0&HVCBycIb+I86JPua`{m5N0*VL5Mn*(j9d>Z~K0q{CN^Inti;{@{NH7Kt2OCq*Kk(-HDha}&$FmJEI;t58i!NoQv+0Pzzl%@D* zHZ6jqB&Er9^He8Xj*}7cVVd0M(Dj+2tXUJdx^kC#UD3Fn&i6$)EA^*a(#Q$$Y?qh@ z{;_8vC3sn$b+fbU3;)^NLVo!We$IK9JT83D8lUP?cBJAh-$^0{pra}#prHo#|GVWe zr21?BfCwkBLnhr33MWlT>_RC!+nEk^E5Oq5oY{Y48(3679HdmW7QA8Q{w$~Z`R#lGLP6z@JPqWMK6?=Of_A8{Ad$oRC@V?J=5fcG_Q# ziSo*>y<+8_yF=!c-Rby>ql#83GLt$rjS*(uI`~d1#Y+Z)PfnN&_oN=iBEllQrWML* z=arEVfpY8*wzbR7U7M8|Exdn{d3o|v-YK0UUso<5WkN1wqRPaX%V9F%U!+ySXXxze zih68-9_w9Ss6^H)nb=^p1vopouL z44s4F@(R&Dj$GnE0O_Mp+j)?15i&5J!@X7NtdU}irM{ei@a^C%CFpP=II4d4TwbJujRV;!^Y@)fMTw$ zprqT8`6Iu(yL6M5Lu}iRe~AEs*ST52f#+daUo?nH5lZb0a+g*iF3yaD24bPy#&^wz zTH9uG>mQ*4QZgaq8X-%$yo5)>jZ^IK$@i025W4q`9aHElFHo-;bvEq3Rmn#P9y}!e zxt$T~ZvsOJBtkBfL)S&|-#D>Ue+1`076%ye|5%*=xGLa5|KqCuaGw7wj6*3R zp1wW6O_Cw14r-JEl09WC7K>gAsg#Jh;QRQ&+Qys5^^@9h`3IhX2ftkN9T*lUB1&z4 z@NeAWBUA}R9QUbygHd1#bMB60C31>07&{}V$xacPm$xv*xv}2e>Gcnusu@7}HS||F z03Jxm+2H?;&&WSjKc?P;Bmj-5!=G+E>x*Ihj3K*T|Gg0daKv2Qsy*08Cf;=1}tQasNP#^-%beN%3C? zSL_QqZfRpGkwp$<0+7|02e0^tmwqhA)-X?F0c{nf(h@SI-=ASA6x>w)DQQ#K; zd?0`DkNP+MrHhD1Y&~S%vBPi`%>L38-J-9OlTHkS9FO$-D22TFn`{{hsPNN=E$@&!7}zcIK(K(K!0$>fR46jeec zepqx(cmfWl19T_q_adLE*97JlvK*~mkDPBVFup?(a}g2urGtz3#n?1@Fkrls$;iLy zg~<-Z=_>xe;q(vaxCkw=Acb}ZmL35KH9oR5+}D z6YyX05cve`arZMg@udpWN5yXc>{l0Fy0fa}JozM@<>dM*tCHWYFt8#i#bIvd-wXBe zX@Whji00Um@Lt8-sl#Eo=W-R>U;>!Umu^UoORm)70F?=Np|8o{JO(Qc*C&@1;nyU- zImqk`R^~^IpTG7QO&%{{h;Ln8Cc>Ra+}gq=ZgF2({wtiTB+=?!kSCcsGB{3##ycOv zeU*IVvHGAyz`O(FJ|8JjYU!kKC#(7XKWs~*W}TgIFvVz!v7C%JWa)i(&X2JZ`WNK? zb1SE`dSs^R-)ByCujiYdq^A9Ze8PPVjj?bf5QD%(3+hmA!BXh;#b4%)9lwH!Lx+ZL ze{Cc1jSrKHraM|g|8C~Q@zSFBmdR!E-ILqO<$S$wNEtAt8t62JJ3mW{a zO72PnV!fsU3-jJ*Iwo{e?T@~4FJ41; zX1u3i6pQiijSPqQ92HB=$akH~*lm9xD^Nhj75_4mUnv1Jf{!@)9^iXq#5d@mHt<_< z5w(Hs$ogH46r}}fgDnYR1PFh#MTk5cwlW{9E18yJSLT2wRC6+^k+~hcl7H_hmc{45 zL%c9&ig28zGd8YY;s||}`~?ge2GjkpIEwu@MrcV%#)9#g4}pfFQN@c1gFW8Q(|Tt? zrKI$3Z0g>xseZ}AZGwShgF9IDOahxe9Z=V|rIz24*i+NyMg!vhF=qep;J1He{LiNS zd<=hYm)K5w`=ibU<*KgZ!BK@b{?5Om>_0@^Ck{wicmAUdYs z4h$vyJ=zcDqIH;Eq%U&l%0~!iR$OEjfs}?}^}|#>1sj_yu<;{=;Rc zB-4R&+F<)5b>4q-7NDqMKnC$EWWB2<@W6t?PoTbG*@>tDx73k*E(WZvX{m#djXz%&89JfrUO{CC0w%HW^6V6itGdWgPI8U(V%K0y`{Ul|UMy3MyR;G^%a z@0Y#4J*;malq9EcU-u2bw6F2_fNi|()&3af6gaVV7T2Bkw<+S|a|7PGEP{E0RJ31f!mJkM(gI}d$Qhre07636V!1WfgJJDnyYu8dXmkS6i$4!8G>t&&@ zEPy~>9S*v;mktAei4PX*jTS3R4rqgrhPv|njeHn)g(3AV=@80*a<}%9k@WVk6|+6utE_=1&>GT;2UC{C^J# z9sC9?_DmPf7!wK5#+S~II|8KeWSP{FZ^eaW`ECy@)B@$lC5+zLV=yfr%D-*rzX*4U4VW9lcXAsy0Ndpa-nxeP5WtH7;UVwT z>!kv@)R!_UZ*Iw^mH+`XC2K-pSxYHH-hX?)Ns=6lO%U|CJ_>bEENA3Tk z_W!?7`~Rr=e^mWHs{XsG!^pu0?+`nMSxu@2NtwtY8e&o+Fm|~OR(7^6>%Km)q{MGe zy9Eir@g+1HqO`1ES^;}{RcLSeyL)8R ziVk%&>YlAyz`ax-tF9RR$;Egrw}bSDi2^Tg2l<$+0HTJ?BN)4`gAh+zHQ3n<;0EdU zK@zt>kx)58@HL0M?xejl6A$MEwt`ke#Td4`*5Z4cbCdh3eeu7ckY*@mBzDq|t^jZ( zXEZ3?2~I?OAdl+l)289P6ID5OVZ?!%^qs=?Agi?Aj){c`bg98HPQJB_ zJFo-y0j%t)E!$uKba+ko{|x5`FzFImH?%ajzA%Puru&#=-YWHwKpmO4Za@VR;u72$5N zh+=a}_2A$Y-fwF~$^bGz$4{$40L7E1*pO{lWZaPgE|=RN_ybPy4R@O7s}PQ6?3%Ia zAL53>(e3XljXriYt+nQ2sk3$clS2F>&}9X2RM;y7BW`N|G;5;Lbt|B>VEs)J`LO7! z-CGJ386axWPNlSPY*N}t9xTyEp_lKi>zkOZTls%`0W*gQAkPZwuIh&%TDf2P2Ent| zKlqK#!AIp(7e0eGzp+vtWc!ipmPDz{pSztvZzO+KoYoM%12lmSxtXhMMp=V zup^U;#ew5a((kKA`V0oVgZGyH49HwNhLhj!vz+$1g;Mrg3m`{=6~~d#e)ZlnDzF@* z*&~hj?&iFGorl(tjP*4`En+_L?Fi(n5}-wvO0<&^9pIrTYrKs*Sg+iFSue^M4h{R5 z9utwlBq4L@sdoA3j|l6Z<~3Hc6Nljmv$>O#_O}xqK%X~2S&_oDxLV9n5Z@fF?%W=h3L$u>wz5cDML?R7nVWv^X>;X&dda_16 zn&@p?1+rIkfEd0EcO&-Xz`)+e5;Gug9%f{uaC_jV@WC{f+fOy^uW2aU0}NVWQZkJ~ zt+kr2pIWS~rTO$luK%8X`8yzHtOpX@tXT)X+6h9QuI7weO!hTJ&cA^KqS!NTGGMI} z7DTyMXk6?h5?g4cqu88xe8D2Pdz*Q8d|81JyZ5pp)`AIGwLI}DI&_d%305}gxxHc` z1dw;B*AZja8m9aF5_Pnk(N(9@!Md|)DM0;O{DZRFp)RJ-XQ;+OH5ery2;J9cnnWCs zh8LdRDcK$8RJ9MSY?&Cst1F{c5yc2FLIUL@fj&&mCDfxLnp<&3ikwFto0kYMafJV5 zF+bcd-hD8gmZQ_5R$>$zhH|K{5Y7LcxsPCaDV#|6NdIZ;@2M^FEhwpUgjaUM0JB5M z35?6YQW#VGJC2u-E64DSxKXFEgviBUqt#r{O9Hfr)99Zl|E&tZ(h=>Fj(jQ4zm&k* ztd?i~j7MXoURqqQCBa#UF?x#zu&|9BLgkd1!VDz=SsZl!Am{K2l$BI1$5G z64I@YR7>F_B!ysEqDyLw#>Y{&ZCK$ za;&TR&PI4!{qMjU12@N(9+FRC0VGZxhfOFA%o)@7UCe!9ma)Xzc|6G)iCoXehv_)* z)o5`EBI8@?UumFJOuAUs{c_?k3#+i?)YxLli1HRfNRH0B+UBKq{;{c4!0+t*`V`F4 z8Wo9911+lR{iwqMrnf&ceO^k-gcpX+TsMRU1K*9Q#x_hiG4LU?_}(u%^8IB^_!Y%8 z0!&8Z`i8Mx8ELaUo_|Ma>+PxM`Y*OMFdA^lbR=P96xfr%ci*8I3FfD?lMyEsKd#%w zRcpz8d|m;bU7(c&cvy~=s-gc)8q9mXX~^lVllr?I6Gw-8c`4AtmZi!hcpM#KZ5_(YhPR7OgZ#j=&P);!mj7z zYXYei^<>VX&hSH*$%_07UU&bN5h7;=QV9F<9?e1#kO=Rqk9EAM6!_FhhH|w#Ro3ah zW^w~FxgA7P@0ajrDu}Ajma1{?H8i)tmeKslJ5=1U>s+IEoR<}j)ymc<_r7Dx5UtKU zzBzTiVXzLB=*R2=Mr}9h9akh0Nd`T7wVwUapn`&2au376eI8} zLzjG$#^ zaE$R;(4)zT9O-{%Z}ks09*?jt5ijJdQp*23)tE-tzcS=BXxyq%V#$ly#j-XkAAMr= zkZ5ukqs1fk_R*}5kRv=}nC3}wfR4dNFfwkOhWcn!DiABwZ>Eh#P{BaSG*cm7K=40 z9BPp1Qd1sX0@xr7g}ru|5qR_;Lm=6VK&Xw$s!D#CnkKjHy=(Zam)Jq3rSq3G^}rrM zqNVFXN2P|&#GND*N9k0fPb?lQKNY5REi;nPB$&! zC+v|Wt2Thv)VyW`{}D<@fnB-hFQCLelzfVT)it&)+v9ooi;>m1ruByDWey7InMBj* zVJd?BE0)X2J%i>l|7E%j*{_X8au#DhYwL8Ur$7-O;_TK4;3YzBK@34Hi&T-Jw5Vr zzfJ=z)jyBKY-UckS^Qw2?+Z59K4~qj*Oj3+i%uAM>i9eJK1U;rg4wLpyH0Wr<^jOv z=H^1p0@9*4N?l$?p|q6d=pU^0c>n>%0YQX>q139xxFch0hW|-b2EHeC6K-moq-uekO4>sJD~e+S$xpy!39QRA1o-(a5x0RfKT?;agAj zZZZHLSY^qps;9WI3(juty(}y!H{(|yEqa^3Iq=j-lkk_1`OC3kQu*>1q_#H5JscKb9{lJT8e&Sf8%O{q6d3T6Kh?p!FEd_9UT1p8M<#P=@_AQGIgNkW-(X}&SJxyyg>k&p8jZuL55`ap8MqHnaZN8ILlPP?j?bqI)x z44L{bCq?MjCZswEmks&x;C`2sxhmkUnjT>`UVtLmv zj5)$Gtio_tZ@fB;Ilb@5Ppe&0w8jD#)Rtg1B~P&{aP^VT8-hBZovja1Psa%N4Bd#c zN7kSvnS6gIJS3CjWmA0NNEeFHDOPBZ0HvD|xXzb8w7;5ClNtv1n*R(3q9?l^+eHD{QhM%e=Os(@ z5AfOf(b}-BVpZC8?~UHx9X<|iH^GJzND5=5vdq0i)*wXp+LLi_bR@c6~ zv~A&)NeNxd7cODf**g?WldIW>GM2@9y=-S~>MT@8HLsh=$IY6UIYORX6d?`h-GWV&%pDtiKRSa$hcSv&I@H;I$U*!(gR-N_!z^GF!eSL7^Vz>wHEe%-X%n0(DS)HSrF&RHk zpq=$DVQ>5%5dx_(AzCGPI(G`bGOjRUFb}Fi4qp%cmKyr|w9*_Hcm<{fLLN{@(0@N# zUcpovHzWvnQ3cvttlpMn@p^>u(UoOJacDEC(t|EX>q03Kh;+brXTN6{r@sA6^ z4o}J%>}ihdESc|=gdfc6oXRq2Y7e@X=4}E=zpD&oK1`x~xAjmmxlS^QrZzy*=32_Qb8aa~r(MfW=1k}=P9hGzw^ChFB9BGq57GWvouKtVmhd7U zf%E2__30`HLfrAGr65g#NS7=R5}T%r)XDZ@&AVA!H!THZ&&pIjCb~PVkF+9lQR|R3 z9VYQPAKdM=me#CA=XRwvQO#%}2`j&H^2=@yCsX0oo^&x|hFq?y5eP0(tMhp)SA0+6 zsZYV78QN?xX$)&?^(y{xaeXjdmSH!9D9dX_R%h+Dcb~weS7~4_UZ?h=e>x=*$J_I2 z18slzk#5Ycpl%0=&Y7}HfF@X(B= zEKG)9PYpvoujci>2;Xq}mCIONM7enii=aov)VMm0vl*t*@M0V7HeULxPkl4v9xM&< zRc2fM`i_zbMS?o9_kFkxz@^JQ-4Yp>$vE2q@I_n=9@Xi%9QDms*;y9BQ~DX_pPAmD zd`%96yz^gWVM+2cBUl6{2@WL%ISU#RnT@38Mp`mPfiKfXo#`t1E1_Q;6xv_@xM;a( zI-gtJpGtbNK(<*ZQ9Be$nowzO=38db8}Q~u?H*)ntoWB0{1zA9!Pb#&CHB7{Kug;oN%@Rh z+QMRsd>AP=*Kz*p;z2NfS;O(T#s*h=)UGj8w61qjM+S0BZYP5=`aw09ryd=>E za{iqYxRpyJm&XTgdP8J9-yOi=7yQWo7yl@3b@U*A-rIwbwEY=sm=gMDE!(#F!BwBa zk&xF1aeZB(x-6kh$E!_bbQdILeg`F_qFItEJ+T5T*KP~y^|ibM{_nGY=x&ZRWUg61 zBm6mFG4ImUIaa0eZE1kH|=fx^hn*r z%gs!1JLNS~a+3z&Rk^`u;V%(IGjhQ89Tf8C7NVTb#N5v!yg4V3mG9inY@0&6SzO#Z z_~az88p5x1Ox({ekP-0Rn&@27143ivFdd1Lu~^BpXl2cF#eKH!{U|Zi37?9eZl}Cy zo8{uh?5@d22NRaCI_5ZL!m(g&laB&Y$NYIFmj-D1X5^2YwVnIO#NmX3RF^-6juvXi ztN>Z_{)D$p*cHDlK4_65D;pEDo3uHtMy{AG+ zDj-{XG~>ZIoVB1>lXmlcZ?XG$t*HRD!AK|y%Lzg_-9Y1P)u3&0b`jwhF>!U?#Y13s zh$D+cUG$27{8f=`FP4(q`hbCMy{etZnCs%*PV2ii5yLlvkcLHg5q9I62-K*-&BLv%KK+U2IYD3DY9q>E)>s+HUPrz*)7#wmP8#O)g|p@6^xQ<&(OFlzJ{U!2 zDepUJkYJx%{-l*n;`&x|CVpz#^JJReN_GEclaLiOEBZqFNn0T?L+zTbpO~C#`_lQO zLTJ#ncCAj(%~QUJ$EFF2z}8Ar5dK;ATQEEMD`*5DC#@2KmP{ezHRi#to*>(^w33@m zbyV%rqPf3rG0W?n#S3SKL|N%4pPx#qkSVGBqi`dndk`Yc868CWHtO}dtY{3$rM$-p zv8Pq@g?G>{)qM+14MiGmZtn1igv){wd%BHwhm%j&+1px1Beg^?x9-lvg=|u1hw^F41+XengoEw>ZlBxK!~_)=i7 zvXXcC-Frbn*Sd8`pq4UXr~7`LV@z_3cCfzp^&;fa(#n&R_f?xyWgoRJPWSdgAH54^ zzBmaRwAk(8Mf}0czw$h^@4fa_$h2^8{qfh+Hfs^_sP8XIzTx$1({m*%Vwr&n4bBWvS#it6B-H!W*WooYD}T(oy{S7AN8 z=B}fUZHAoh!57Ei%MdaAP9Gb;yLJ1)u!E%{xtiq&2AZ7!DSN)okxbvGai;dD_w{SR zjH)@pRr8JWne}~IF66Y{Q+*|F$9cP|gOWE5L>(q-RQf)ZH#rS4QwsKw4mFM+sMeXz zYWDE-smDZPHY1wjPMWl{@`F->B5&hkF*~z5Fu2Sobp?Lc=?5a)y1<}J zU4da!?$DAX$4ZH9Z24Hr0rhr~az{PKe0F+hYq#V;p>FnSRz;R7oR`*h`2*xMEs3Bt z6I)O2#_bx}h;tPeC9;-yRRqU$KI_re1e#{#By!N#vNJA5b$M9W6)|if3igf=LPw@3 zzgMc5aSRLI~ykNJV=ub4j;j-*rcIdXVEp)8Iza zMTS}&;J2;n?E0dEXDWod@8%6}tA&aQ*wNByssrH;B%j#MayVMyv(Dxt zzNqf;y*q&LQ5%-rD=!Vwn$6b<)k_yUoSC`i8)Vk#@uI`a!q027O=jUiuFL4_i z^`&3~z~9C!X)E4%(1%@Q6TDOs!DNa!k;`*)K*l)(l7=c~BBSwIK3}y4x3!jCn>WmSU=~%flG)!Zp1Qwm~!WYn%&X*L~u&ohuzj{X>JTW>J;uG743BEWO(xFc!YCV1&;#M8$Tw0C{4 z^xu0IWuY8kXVm-xVsUas6;+7DPCKV}|@KAa=T-|D{y;3^lM!DBPe)Ge`! zDK}w~jRGURcV(VBwxna2g@ID@NPK4@f*>Qnn~ZaE;C051_urUEERgk_gqjju9M^iv z&Ou_X+aS=9S*x(DItvl~~1$$yHSP$fB^}5mCtp!>}3D6NY=V7;3 z%lonb<>_i!97hse@%@Xo^atDk=fVxA8d8Ss9Kafb7*}jZ6z^q~EJ5I4b8DZ``n#L`rS=i-QZ^n#~8F zbXfb2Ozc%{goxAH*Q`t^Hp8#=LJt?nQ=$b?#7Wzt-5|085kojET+Py{GgUF`o?7u{ z{JDT4?QBg&047eW*tcTVUQnJ1J^7Q*sh%4&a%;vXm zmu3JNf79(ak>Sw}gfW#8H`h-W+%ak0Ajb-#jhc(@uEw~Yw*E^S{x7Rl1qxd;csx_4 zK(3rS4ZI-#GJAs65*rft`s}%`fAMxvOrqq*GagBN#`@htae?ggZf5qiYwYH_Ae+zL zq!VZ-x9iK+(ZR)Rev4u89#hXP7znw|;-2AUwlQm71!5f_CX|+}bohz znu6MrYR|q}dM$-+!K&Gtjr(pVxJj!Xu)S)0FzrSL z>1CZ|s9!wVMQpe<`zOV!pY@G2%J(*2q$Fjm zJkSUCS<~z-toJskZtkX7IyF9BQlR_SYNhlCT#>%gsRx5QXyxqxcIm`?T2=Ys15>+P zfWjJ8Ok{kfO2HfBS!h2Udb{@WV%A4`i3bq&$|>WhRYb<=6dqIBxoSWZ@$j$tmacn1ji-uKXldP_Y}IbGp`0t-9FjKW$ctG-AR}}ZpdlG~+RL@}KXkfy zRilvkYf=gxD>b?bX)N^9Ee?9ErZMUoL{z$!&CQ*(t$Wj#@;5GAhNxiWeu(~2X#Z>M zLW3Tz^qO#?c}d`Y&;cWY(l~W*UsnOu?WAAx2h><%TqP6|gt6^Qw`(}G7`9%|Xs+1e z81>%E-)QobYD)NBsR(=)-0%Y^bM0jWk`XBlhGAu%A*#Vbv9eRsB zxkP^KnmK*{KCaAMa90WweN;Cv$h%bj-tEDdqCcoaB6j1ThI%$jJhJ+AL z#f!)a{gEQEim1~$Yb>55qh4|*V2&CX3nyZ+FYZ1S9U2ZR6yq}$+2dFoEmdm_@eq*t|ptcYrIoL6uBXf;OJuy-~J`&{K<(xPn|KqPcYSiQAa?;y68uw z78wn5N;kmaV5RewN}7BLCTy-zK5+-7pH}Byt4m5?RgM5#`mJOO7*%eC$JSt={IVPz z4>pijT!tCt#?X7LI2lJDRw)qaQLEaUg@A&UoWNn^YWXAiv;q@tCQCNU-gMIjawcUs zoJm4*d9Z0Ykulz-g;Aonz&nP@+TTwgvFyR55vM_^R=Wx+(PDhq#eVf_)*LoVOw{_B zLa1b4xwU4#OF*7qE_8>-q_$d}jz~3A%O{y!q1|Ei$jc@^ARZ{;h0bTqd^XSZX6uxe z;o0X7WZmJU*M9-?u>PJ0WSQi*NlW8(xYE)u&*RUX`4CqtGXA)LMP7xp|JF?`xXliQ z+LZNNjnxbbsv-ZQt_z$A_I8GUxDNisYbgK;fWM@myoLj?&GzNl?_f3@`XM*y`JH0M zMgQL`Q>~-JCH(&;)&Q>c7VxZx4-IWxw7`Nz4Wd4tr*zM4GXNdn;%C5r5IqEc8vp(* zFaOhr`MpWb#;zz2U^tLYvy9hVX~s5cK58ffVAB^c-TfC7mlUXdS=SD%{ck06P%KYwY6kmDV=DL3e{?n{B~k`N{pbbLaefTA$dCBZj>6GN|IziVoB-Y zZVy-QC~y;!5UedZBTzY5B`}>-2ke|Z{9crh{VZfa9vl` z->>~opQs@P9_W~QJ^$f1qtGv#oBx-(`QOxW++SJ2`_UNcQTaau3p^$Av;T)!{g)y| zMN<2rCi;K6r@*UnQ58mnwTP3Yd@cv_de=zhwu?Ghy$EP_qiU## zRztMh5U6K!`A&77r^L^ucy#wA2uGs7D29IzM~3HTPN3Yu(GHPySj%u7 z*keJeBr?M1&m@~>Nt_?*1bJwqbJHj)yNExP{p`5@9whxDimlhpu~@*s6jDuxlHb)^ zQ>FK+&1KUbU{VySC}5YTr)yVeVBgmxvR^R+r_i&O1ADk1;ST_3U0HNp+LH*c^>K~$ z3Ng%}VZ4Attloru{YV?YsgZidv{^q(x3AB|0SjQe-11JvzkdSHxcs7>i88Sr<>C68 zb9qupq=Z4z_hS@Fye8(fM=N{`vOC0qAKtG3U*Il!by~1@riH{XwCU6wA;r_awKqJ3 z)Nwk!N(Erbz_s>7EX#z)gAj4Ssv$B4+r;p8}LFWAK9?!xF1Zs#ckN*89 zl5WP^zizQ?xXix_uE_fYxi@cA@?)VwyUNAm1W%-3`5|t2z%wENqdKjcyQaJ%Y__Uj z$hCA+#k<2(Lhae98dnfa|HS&OrGNxz}%k~^eIGtR5tCjsyKfeOFE+xCrsf$sCmc!ET>wo&ZF z>dVha@zQV&y@C9Dl5KnD+!1`^CpQb-DGDd9tw|lI)nR-mp&T11Ctc@E`(f3@p6r*MO-8FJ=9H1Kf?ChV;$6MEB0L-EP_nE-2 z3_t!5%AJ(Mt5GXKLUuzfwLmMRL*lVbUv!fPi1B_^(xC*@s^ege0jUzFW^Y-tmoHVz z^X!-Derw>ddhz)=;(Y(H;g=yqcR4#^32`Qs{~~eqQdzCLq&;;@VAbk9mys5RH(~bG zaOLW8O;77o3CCH|cl4`f9D3EkXTi{nEzdL`jTsrH1@K1C&C>35u2qXYZtWPHcW~6U z%$hCnaKB%t7F4mP&BymR$lGbtY39|u`o)044OE zjvpvfkz~}t-nd3zaxWzZRnQsa&kCi;bHev60ec7i`X75I{nX$9q%_Oye%!)|GxIZP z`t#pdTqRUhHl3TgW)(X{wda2;p#Cuk0Dl6!*`68Rb$|`aIXQCj9TW~^Yq56sjG)<4d>_>*;z;8 zT*GC#R<7#%f5=)GvvIB6WD~q(n=!u2>tG zAz6Q%pIL!3rdG1YD6L)(5hYeq z29rgV@hq`Cd+Re7OLE;M9{vWt{zQ#50JXJ~zMTUGIS7bxX^@z;2=J<&=VPU5!Kd$) zPFD{9@H&H$TB67n`ME|F2=aX=n{k^Sz%Dp5+!7j7T^iFWJJ`qtkV4Ro%|KpQqOecu z)3wql?!EivcdIevdykq~+B!SKZgwYMvlA7RANp-phJuG22=VP2|3KAR(y)<5Y*g<` z?WboQufSXsX&1a@k2w_I8RHi(eiD>tzvX_epmt4AKX`RdK=G8HCa&{Bx7sTO0FepR z4`+LEkN@0dwJE%yxx&bs>{8?>RVTS(sD#jNN68TXz^z7MdyX)6`#mK|pT%0Yk#oTt zGlwIWBJ4aWqX5$3T-JAZVm1QAeg3WJ(;S|-PAbnnrBEE@+xz8A5=q-R2cawvJgM>T zUB7$!^jYom9TIRGPMg>WkV=1RXdxBFJy9+i@*;fjL;17zci<^s0YCb2%5#9|%0Xcn zcz6xWJvk<8CH$VHd}}tVtu#@RmSuPcftk%+XKaxgND4eosXsUsn%90Jb)2+CYP3-Q zV#^FalbVtOpUji5Hh;OMW?4wz6z(!uj+8Pr^^}E@k}lTojFW|p%P=wfy(~L98NDHV zD}og*8rOe$?4~0u{l%*(Q=~H$HDXJ2I$m(~{QoMZT@R`%<*JzF>6I z#LCx}++z)I0fl&Y0sU8_g%y1e2F+PS&*J7Xkrtx7fmK`Df)K9@*d-tO4eNj>oyh&Qs2}d&=8)R(9C_Flp5m(Z;NvA z{r&#S^mLqz$1Z7aJqs$mjz94$ONM24X5q<04CILOz?-Ey@zbQSkavSKN^W)G&WCES zx4AV(zI4uKb(NG-8Jt%IRK^#NeV@I3s4Fo7)BR@WXNTbmz(lP+f)-65apNVtbRL0j zm1TfiO4NB)-d~oHcST$VQ)0f{x(B^?(EfR2F5?zvAIwfp#BQHF3p4V2raOL3R&X&VDOM%GwInxAP}_Tl@}HZ$-|16 z!7k7SYkx?TxT-53?r|BiK`c-3lgy8RQ(9u$X7B_z3fBKr6epSV2Y%cd=nxrm1L)4v zPO%+y4N9>c`i%Kmd8u#Y&Pkp0+Cl3wYnH`HbIHs|(neDds%$WSK;>P4Iqh{`{hpo| zqlw~idy@)76^HKz8Yf~0v}+x8;|`l)-s6w2k6ptWLvNK`7u~}RV2OEs(GT~GeTHtlN)KG%n0oXRQ>CcY}H?xJ+F=PWdt!TX6*+`*k3od5Z} zz$>QjCg9(w5MYWMF@+x+(>)O5!tQX?{50i%!*$qURGOd5;`%VoBJRF9g%=G*em#uS z{`P?RWuUEA_IvYdY3c8M&t5olST`N%wc~4J*~&v-W*P`Zb+w1ZtR`KTS`)W(W9Tn- z&k@}~2MpR+NP0X9TbXduKBr;DrytTFGj%!rOngvR6OPGG5`uHXLY@ew?55s4dwL%H zTM3vw2A&xq_vzCwz}j7q=>hFZq|zT;!C!3k`KhkrrVcYpTdJHTeg78x^xRK#FY z1o8VHu>hyl5vwNbS&((i!=^2X5>JelvRVTZ&7z@v7Y*5DILrm2(zjjya7M!Pn&@@y z<(%exCN0O7>?jYvNwL#lSXZI9(rO}jQhNVa7x2{2P>^Um;#7SLyzsZbhP2DzJChmU z;hh7Uh2~{C9q!yc!+PhHf}ny%(M1L&>={2l4>Z9w?qV zg?nsH_I9Y*E&x*Qffj1KU}nPGxHyUr1M|V~^?xFi_5K+>rtHnfEQ`grS z=^|O8EHdmKTLs{ox3@(VvPWRE6ciOya)#fq@g6BH;fsX1e$3aXHF|TIk;S@ z^!hC`HO@fHLBihZRCCQp|esY3(^r4$(D{+vAX^ABxK{Tr0@|c zHTYogoxPLgXQQlmR`d%H@Qh;jUVtWXjw^waf-M7<1hakfF|FPJUkTOIz8vfvS++k< zIh1b}Z4P2SIU8V6|9y1e2-WQIbGCMxM_c+AEa~PknUCF;HaX1K_?nU-Eh8@$(bYEX zJkaJ}VCzi}cY!hQz^!SvTnsH`WaRa7&n^R2u1R=X0=m^0>^a+4c;He~D^~vQAaFMs zJGLsjv9v^Q_9f~;`fPylE2{k=R>9416ER1>@yZ8x%jzW4b4E{&0=1)FYii~0UU~cM zXD19Zso$&)9QF9b0ODTl4lYCIgiDEOdW6`fX*jxRgnR7_3`=LD55^A;@8uc@cA*W< zQG$)|nt&C7ceojT9w^2CeLD8rAf&9X_o=))>LXnm81niZcPOjs@ml zAv|IsS_lN92^|e6rK&1S~{y&XGZ; zX#OYJ*ODI6-ieBEoZyb`qvjXL&o@TLxU_UB_0A%ZHvO7DaLa>d^!)*EA&C@pG-N*v z1ix#m7gebG5sBarqa`e7=i+KyUG0+}K76ct_hIIjSc6(4xSo2^7ez&7x$`GRkFG(> zxH#n*Qfl>j`6yR3GhID2(^xVJ1*jB|WPv^)2d1?rf*eSBy7pC|*U|O6W-N-`)K?Yf zE>h;M`Rn_NXdIXo8Tl`8#JMbMzWFh<|727+Xy&2EaW~_VXSP`CmfV^vf3^!N5>?-5 zjr1zXy0??j;!z&uXh%zJ+{>KO*@Hxi<}T-t1+RPqnYS1=S@{TvH2RypF-aIXL zmPy`beso}@YuP~=oavh}+!Qhv^;A83Me-W#vA<4?)F?E`2Ku_smG8=6MR6I=75A2^GVUv`ta}qAVhTM zL8!=7SzlNYEZLJQF^dXCuCaGYxH5`H`Diu!lp6Rp!jIz9U0Jp5tV(M(I&_ojA^RiM z%MZtjuN{?Zv~0y>>vB;UyU553?Gu00rpFD;AhYD&Ot`(>XXuViTVoU4OLw)?f2qpg zV;zgOF;k0H8?v;LSmdNBd9cq=&nE^r2XQ8OTDo-E!tzh{nf{3$AgNzi=f8ny?V`}F z9ZO@&uH%gO2%=M+Rh9@_F&90bX-C_(%`>&FA1>qFffWqj`3{0$eF*J(6E3ogi={fJ zk+y^2>HcW_(b4BG>4DiYqB~FRwPtuFq*laO_>jZAu$Z)|VXN|K?Ue1|RmEgXkeyUp zE!|FJmA&BHF>m2aS4d}lbKdfi%ldUg*M)~~y5pRV4pNJHOMOV{&6U&A$A&M;FJHqr zNh_0hytm=1{ye`gZ;F1~bda#!#%}C`&he}op>#41c;uh0&v-Ha(wi|g{BkHE4+U_b zazNwAPoFJdA-$NE9x8mo!Du_46CpK}lVsv6|8Z4z^U~Fr`DojLas7`Y=P~i^2{xNj z(t73^4c6}@_3%THaktG+8e(@(&b0J7Fqk|L0cd{K`Z4_^j?bGw=noIX-ycQR9e#h> z*4`1e8+dbLSr{2TmlSwnNijj0C?3nAF|iwU3Ud0;W339ktrqQJkJHRMOtL@|%BIOsYBoYdo6oW>@N>>gY)dik3B$0dhcF^HRNpW2 z#GYff@SCe(zRJ94f7#ba(Jpz+y_s=qCH$c4!SqZ^FEslDi79i(l6`Kqh-pGiT3afa z9`UGWnnotD^YysGwF`Bz?HeB#8vK`zn&Xo;Fhwu+mqDiLy{MVBhf+i!1U%k?nF{&D zFJ%!{BLK0W;_|n&-`Z-KACDq4j{ey|ObQ~^_W*c)x z73G+b24kZirE-1X0G?V)dh2DQj%Z!-B{oRu14KonFx}EqijMm5TFQ|{uK4yqd2goO z$CC;E5+Xq4BUSnXekSu39FPBU3+Ubd*-BO)gtl#>Di#z@uJ)>-?Pc=nOJD&$XN{mQ zRkO#$aJNdnq7+%*;9Ow`=di_aJwh;wpJ}heCsm=bLI}*(|0S%NgfLs zj#)xP{7kYrA^h(1@si=BTcxbtYKGcabP=4=9 zHOHreje7PmG@h4ioO7qYHqc2a9Ux&RhPA3~%P$=LR#QBXgV5|jefDT|m@A6f zjN4O0C!HnEXLxl1`aVOH9 z142~0c!8z?~ehuH3^-g zWG9XHz4N3>7=UT&*b!N#8-R`ax%+1dr=`Dp&(Dd+43q+j%X-&;j_!E>Oxo~OppO_l zsX#0`bIheF)+$c&{(Ct^IV*Dq$vE!NF39brrAkn#Bq<~g;c?Va8+Mc)H>xy_p&QID z79YqjjG5{1R-9>~94R}c;`KCgbi5&M)ML-BP>Oey10~_gx!>}=$VoS2*lhZw*ek!| z_=J%o@2jR4G!h}11Y>WHj(A+}D)?lrY(~TYBarDj=_p*T`h1w@PI)8G7|W)QX!enz ze_BJmbt*I@c)g~ANH$mc-LOO(y@C_GYJFMWXX0P9hVNguX{Sir| zRXws>p*GW#d^|YoFxFHLLRrDpX`pFb#G53AXuG&MjeFR&+<_-JqDbw8r74H&_fhV{ zU9aT@Pw)hMo2nSKKk>%jn`P{ffck!;L(8e!FgF@8tC7$Oacc`2!ly7xX9t z1d<5t{5pmT(BO+4Ti!f5(bvLc8CDW_If6+h5%{7&(N2A^#fR(ce?tHyvA>de3wg~J zIg>gsxmUE4PSu&JNwCXll6IVFM_#;Y;k>>J+9s8;2&LSsk8 z)lhY{5l3rj?wCsv$WUl|g;k;Xiu?Yb#0;yehr8p$B&8gD8eAIdY8{c1+pFq4Wuzev zIy;k+Q=OZ#?U#fw5uufb2sl!USzSQ@nR8@2lC2OWbDDL+U3WpnA33UaG%B2_Y`Ul^ zrZw`WZO2EV<=|DIibr}%bzUjLVp;{3Ro>bL;4=f(-mtL5%gyv7nN@=xqv@c&5j6j5 zN#kpPa1~Hr0Kz>D#>h~5S7X@STc%?*cS2s%g{FkH%oX%Q(i-%;8th`J>ae41#g0*d z8+#;2<&w!_X9msgp&Qu11(O8#zPWz<-I!gq}v0(Sy?1?M!wmjx7kuTp75~ui4 zPYxl9<^Fma9Bs-B-#0kOD$ewG+`7m2Ty*?FN2Y)JC*EXCFquS_&Dau~`j1e6b+(T) zj`^JS7^6JU`OK9rR=o5uT^@nv%>{hy-0$4h>L^fD1Vfhw(}MqH}=)lZ~S`kX6<5 zuJ8Pu8T_47Q!gzY>3a3u$>z8F3sLtaG9Mq&nl-sOV|e%GHrDSgk+|5tS?lJ7s~htY z@UB5p_q{y4rL5x(+BCKLyQ4F%^L{Lg%%7?e2$MD6duh0xuy9(}g_wTht z=4RBbCf0N;&r9!Ig*2iwRpb|S8?!4)wKiE+t=GHRXBL|h0_zI)pFr^=(2h>5M(9CT z(GA-s|3Gv%njXg1&dzY3osvUeu{}3c!x01r*!YexX0vAX3mt*di+0K~)jW&QZS$g1 zhwrVgJyX5GG;D~1FOW4Oq^n*Ay0T}rp1GyBlvQ0QdkbTrft67A?$%N}x*R*a2wb#M z^Z4?_G!p`>^7Hn^Z*5>w zAFiIEPOrXh6P6s#iE7~5-P!c*tTDZ8LwT}>@nO$=fty5>{06sy_&3%7ev5Bmx(V^n%!N`w9l$g7s?4jL;IRxHlc_5(3 z00E`s%ko|fWML8zHvQ|EUBhuB{qH$GLFPw7zFKPDtwQ%Vw2y>$bP1J@6smeIE#etw z&>sNYZP9N4bR(dMlKGVoQM$j9L)V~-LK{x1G9*9f)PlP+hR-iZrldrOho0*58K)7} zT+)3j?3C}cb%-DL-BG}>@a63|+j~2wcV?1XVrXhoUsGM&-`)4|*eT5EYM+RCM0eL}Y+IXK?J!fq_8YOaY@bD7Wm?z(d0+fV0$Uve)f;sc>Kg;+RptT%fF_3TasuG%=PL`vR#?_(b zWwW=#1ihhP9GuY>$dk3_U#cRMyrrsLr|^y@1V=xPPjrLSA2P!jlpQh*onY0~5fcx^ z?)q%*zfK@Z2I_gO=&TYI1-u(?S|=4s@(uJ_Y4*Uk0A@9jr(69g_{r-PRlykC(0lt|D7 zmP_dkgE}L2it<;)p09KtiNPA}isJY0ttKyW)%hfxlut@zdDefb6?eBkC~VHl5NBBI zY4#2KlnL(ky}>C^35pMdFVmzAUk0K>Zh?zetaR=(Q_^nOR;n990(APYF_LtB=JC)% zD=Qebbb`d3`lT+qJ76N_{pZZJ%oDR})9f{#Z<6Npb^}e_t8q*7-I63Q549|xV-wef z%mPUdBcT+f0oNOlys@t}VTTp)S{a8VRMj?MyevCH0#JMZhtoN8U$5QH&z z>)oFePhOYx7yb%5^iE&H%|o-pPXrg}FKT4H@{ZvR?V+E{%9 zAGnj6h&ng>nB*F(nDIovQvLSW?w#}yF6pM8#V%vL2&@{b(zu+N(BsP{Vn zkjBOZfJL}v@fxtc#UbebzN~E2cf1WUB?X*>vBszr`!S^N(O|x4+xtgNCl#>{h=n)f zn1^?l70jr08!75BTZUSUq`18Aiv&*EVP1(-E#>m3%T%Tn#|G+>P znN6 zGWev{l(RlRY1=5CB^gA!B{_GxyQ8cUxj5}2j>#w;Xq}5_+lR{2XJorVG@0Y75F9*_ zQ}&Ql$-~X+{hk_FWwLu^C~0|V=wqFwZyBs+&P1h1Xa;IV$epBC0}X z&(Eu0Jk)KS8lru4DJv6oG=cmfnv9uqRhtcCC?p=Uc+U;3TGAjEUfRr0$~XqH8X{vH zx;%lXsJl^RlN)@b28d1yDX2(4OTsT;fM}mk6)&}NiiCUIxtNvN7Pd(}lOkJV-KaS? zRFrhvTwCIKS%d>ENBQMCIM<=;;2n31PId3t`1wo3LRRLrp||rX=n5N8M#F({_`wmg z9M-vvs_szr_@+z$G4Hvm0xfVns%+h1a>K>>$297jdtNGtoA%2Z1% z7w;qmCj~w~K1&86618^I0A2$*Ae#SG<$zQZo{UuKUn0xxeva;vYmBc}IKeEAlfc>= z16=uBFSDkx?0i4-xEqxu*(ibZu`xZ`u7PENUhbr5pl5Y)X=a9q>9izM4nC)WXb=97 zNSRxt%uZVN`%Izf3{LUe1dB}w6R*;d;OCb_Evh2Bzw4`*|Qhm>jK6<<9 zCYij@r#?RRUSFz40Y4D>o&yb(7KDEsn9|j*^I<8C#qt&qEkvB%g_*JC^Y)%gHWL{) z`*$?UiNhYd8DI|p{I5n$PryC;k(@RmbBx~EA@CFQw30S<7+!m+Ek7S_wN{yDW*Cyv z$T811yLQT8G{jH7AKbX>E;(r;b`{hX{Vn=ceY+K8M7 zOz?vNH#Pu5B$bF3QyPol#|0$SYf+U?;CaczT3Gr!UIn_^h*1>wNl|Dy=U%Eu2bi;! z-wOytJ2X!0k7-MMi@d_fWj^K>RYBgfAwb_hgSaTAUJylpSo_rVwkYbal_V1g5?Px(CaU-r$W{O{2^zH zB$=R66ICNR#6qp@pL4d55@-4*R`(6Z4N6sgk$~w8cT29)zv<<*Y)jIK2_ia1WJ?w# z3b;P2Ab)N&5^T;F9@%l2LZka+A!GSKWg3Ftg0vI&QD8_Y)q^z4sh00vsYa@DX-%>U zrVMl5!F)UQ-PztB<9GD@*rg3)XD_#ioX4tIOx8Z?PFe2{gSYT9 z66C=0ea3f=KHPTG@cnrA7lmBxWuTd1y=?ZXWM)La%IgSf0(f7;4#Gn6q{23+;l;Zp zbcPv;yNg?>%$KE!h@zd_t9LQ#nPxhj!iWO1P4V>pQl!V)o}cN%@3R(iM_>b&QJ1_BTDR$;&OJ31o6c@}3{|*nH8~_!WD&52boCfhp zkqMoaJ|ANj26Uvw)Z>u%y7c|(^3L0g{Vj*yn$$F3t3r;;*LdGzA-eBw>I;N_Ll7En zZJri_^xwVUTpYd$=%_}2UQ0{+m*O*9)5Z*m#oFlNsyz7`6aFGQ3v2UR zS|jD0810G`ac`UwkMNIyZeH+AsgLC(&*tv`toO$8=I}5;I0%1xZT^ynO_LGB>(;At z)nbpp`Et1sJlxhxudcn>57^(1;!Zz&1B#4l{SbWNOq>nyySbnL#th9oHk~TUMag5f=ZDZcN|enm1TWB@ki*U9*JkO@KgyRsrgGTW z_gProj5(&al-t}llST9T4yv1c$D;17+H}O;fI>X4M(JrE5mH7C_I0{^)&V#a)GI*3EW!4GOg#K@z49=(;vbbJv!i8pS@mVT9WVF5xq`KTlai# znNO|;O9l@scy8N$-m>7rBcZGpF9=e2 z*|WaIv$8(~`C~AiO>aY#;d5!@{u3qdXWJG|dD|s5Ej@*_?K7tMkEQcJL>RFbzQ=K$ z-{#-u5WVDwKD{ZXSabsOL!9JMQB&s9ThlR88nWbw;$h&M#33{~E#|ov??=_Vt9C(s zF5Tx9SFT_|B9lYN3a6l#Pl&eSgycSFcvf0@<(8D{LjGR9wlMFu-gM`We2p)IEMBigXllLo`)ua{mKFl^ z;JO#J<#JN0yEwB$R-N}!>DasuS`<*Cw}N97Y=x9vc_Tv{M>xz>Wp zuR!H)9kTL5>Bj4l#m5>Wj$Obu$KE%5`tYTGs*>7M=i2KVN|!7etudYw8KE}HV#2v1 z6-lD`-tTnL^O{R9^1B_XR1V&U>VaFy$TaYOl13eT8GuA;NpejCM(vdPYSKekz;m^l z+ecD)(BBfm&V4ki#YZGv$X*e>76CuvhTNDQf?hemQRwZ9oq~5rQ1q#KcL?~!p>}mbY>MPGPRDeFUq{~D%(@E zQ_R|o*?>=AAO?x^J82TdR3z%u}o~67ZjBgNxDd%B?muTWquZ&1>c5eDK+jW=G6U6c0@tU!u$mr4y^3)3Pi zv9UYm3%PQhGD*b8>{!4feXF4&%f!XP*m|Hix+?HMD)c_PX&&8mC=?=j?f|DGtl#~+ z?E?Cg3>`)S%ZI@aoKHfA2}D=t4nk#{Fh$dJki0A87_9O7(oSsz-=o*VA6rUuFFlv6 zDncSAY~hu_fWRhU)NRsl2(aAp&LXicLdkU3f7aMikF3#f&9mV3g#0T*0vY-HdQa?e z1D6R|{WM<%wg+nAKTlp371RtynDL~7bzX@#pTJ^m=mX+7K}u0=+so%6sXEh$)~ z<`U&)(Xi+=dR44pg(2tN?uyk)grqH1`BxSbHrFJVT?|V4WjVN7KC{(Z41T;&u z%YIM?jm@FMQJVBzdB=2gG`ZfPI<=>IW%%AN_b8+_66#B*;<`gr&R@y?>@6#Qrnx+d zZDJKIkcUPvaVk@nNzt9Bq#sG)?hZLoi1D(UG!55mI#E7zUxl&N4k&$cK@C0t`R%bm zMgi3Ri`bi<9`xgYnt?(f{Q;2+R1@a|gWtY`7hzbJgpsPShCv%B+non2M{7(^ zZ4R=B9hzg%Xo5$TK9y)2_AE23O*`r{k?RZo2bA_e^YS-ts>ap1mY|P877!GT^yhpo|J2AM5~b*)I|I2tXae6%mdd zsB-9Y+}k`IW84x-fXSd; zJHZAtQW%0>9*B*jr#bin^~=<}`JgABH|pZ5ch`YezSrh(o=il=V!yPudJd{Mr3EqjUgJGUnEinS!pwOrSlABeONtgof&ijY*dAt(g| zg1fw$3Xd=nceQc}RpkyA*fVNq-D#0et4|In(?eXJR3?lFMk#uf@oo%&W0>zY)Tr_E%d+6Ilk^a<3q$B07ftbK zLJ$^O|5 zRTq@%x>0>pYq=j(Xafz432@=7&;~ky1`gylo^(9x;xDr{J+k2f%69tisi$q(Cs524 zxHj>RpYP*%JQnsT@HZaM3dE_~Asv@vKNv5&wsrM!hQ~#)0>4+5GX~~lkWT&)1%%CJI!yF(UdYm!aH%ek1-N(}d5H;fzNKMYZK>hv~c z6V1K{M+1NF49>1ULyn(Aa-~|KGOcswqwht^n4pE{WPE2pSfPqTTO^dZ6W@yTbDyW>2#J1T6NG$;`nEH~mS73sn@7sjC$(3+ zSFR_4;HI2bt%eJ37jXRheoytR42b4#9H92s27X918T&o~j^OtMev2RAH}EF}u0Apm zv_O8*dmUZ$BIvkd^3&d3Lm|tU!!k@HVj8g1htzGCX=cNRgY#5^x?E=RitPv~@OLJe zp%fu$Hc8Njc2HkF(1TmHFh5662;%&h?LHpLW=vysc*sYh$0h+DF#=bvZ%%cG=Uh)2^J*k@{NvHAdi+lAK3!1YN+f9 zm4U0mKHD2rj@?-^V(vF=Zs3znz4Yf~ z>ESvpuf1>7Ah)x0W#wUZI8P<(twAL{`=W+zU`j(-)DOeV$srrY`}}JlKz6iMGyRMi zG(fh8uv+n2;BE&hU5D^Z%$Bg_Kkp%p&3Is?yCHETs4Lk`ckI1$g|g8`mxGEcJ`^d^zWH%Ju{`L@(o0RUOLv&?JcSuz^ci_) z)#cq;s;k}iEzRS6bD%13FFmB$5(M{4w8oOxUm*Iy8hnSN+x|cL;=1r0)iL+Sww3l& zZ>4YGe~V&{jqmUWk^_0wRMO|mVWvOsAo>SdJPKIf(am0_EX!iP=K{{99`fDhK;`{o zjg3{E%`Liv?hQ{iA<1EPVWVTX90GA@^BqA6!oAjiX4QU~LDOs1&N)(;fX_W zYI$OceIZD{i38Ko{~vpA9TjEU_KhlngrbC~NOyzMC7^V7mlD$5T_VywBQ4z^G1LGf z1}WXGq_lLyKF9lc?^o~dx$oy)dw*;HvG=#utZNMe!*yQAdBpF?6W{#gvqPwCPvumD zR?I#sdy#loz~riq-SZNRt7Kp?f_MQ~>yyrUBRv<7`ks4Us)neTfI{h$(M$cqw_;*A znh&vc&VZjuA%eZkaI9;Yb@D$VjpZ^5Ea)nBfIWd!J>`=q>~hkw+FllhDl~H$pcl{0isrIb8%+0fGl7ywoOnOalR-i8av z1U83#NsR4}hAaRg`FBM7(x)`5m0;l@`jAYD_=YocQ`xg%QXwI)x1qJe?H6nMP}B-+#cxkRosLEg`P2$y;cZ zK;?|;LDm_Npba_XW4}nf3$wz-zG`?CCJ>7?z9g~TPox;~7~q8QY=y<9&Z#LuX#uZC z^j$|M!|Ii;2hkIH-2x*J!$v{944#^&)t_1=(HHsUC;{5T2xNS($o)Usyizoyo)^;l zk;|~`KD}{|n&x|VqeUE0lGL*o^x$D}Q!sWZq~9=xsWzFwsP2|0Ywf5pHZ8!Tr$t_+ zQ=Mv+Lf)Z&l-|Dk9C`TqL6N3M>A{!n-JLdN!&hfJJVFz)5;N0$3!|9vNS=#-3LUlq z*q8!LSotk<_isvTiK%ehR^!C-O~aYO?PHC@9<=hzIfgEL5|?_BXx>qTN6?Blg`7DS z!-f-|%<^ctl7^B>@}=GWw|%WTF~@nzMU`RtdbOmj&~NJA%Tu5eWqF7_!3}gLCp1u` z07szR{G*c*;PBPIh7teGT7G&Nzg#eor#Ruv@tdz!(G4yFx1fLPJAlm&;&s$Q6nTC6 zOG`^K5}qxrzN=DI?d>1hsEhg3cD?bzAHH_=>=8{$fujvD(;kw-@67Y`#Ws8-d)|H#&C# z@$Ua+S-LwJE6-au9br77fs(~eb7T1n*6KZNeQ#rM^ND<#ahJcklgGsG*Qct@p`UoY5AtZMKqi@%t9fmYSsR>8x^=L4L3r_&X_&k9Lko z`T0$PhikTnR8_v&zY5m)qyoHN7W>TSsl>#pH*o%Xvp??r&L8aGjXN!`WZS#DknX z{UX9l!`wWIt1NO5M)*K!HRDL2$k>gXYUvo`pyE)mAK7MO!Z)w{jKKYJxsD(&`mb65 zz|a_4$fMh6I-jM8Q0nbcm9Xdej!oLcwttnPmWftcQ|a6cR;jZ> zS-eCD74GaD%^sQWYTQ zE|o_qB;uq=8u`-d9&^+ED!CHDZ)ZjCD@Ya4-wY@1)TLE3c?v3?{tRgMl(M+}?5aoZ z)-N0R$B+KV@o{{WB^33AWIP!$(9IOu{)*Uv(_{^H_orTkvsuZ^X^pBitOCg7##|T< zvN6S`>-LJ3`vn|R1+Gj`&V`Jz#~WxDZg?+@+g_^r)mvIm#*_%(qytmfUy0vdWWi@- z&jh{Js1u76X17xlho}lqP+=3paYvThSLNS$a;o=SIBSrpHkrB9U2XfkC|eI@B`=e2Xp(@NXgerqdunycku z)2~10NI)XVdtzM4oK`8TarsVj0t{myb$2Kb&0%&R0#gf=uP-wP0#&BN|5(l0u}iw6 zvwbH{IWw&&vKUg3SWPF*-ec;%AF3DuT`k4>z+Y^jOQS11TXaZxd?IBrg>Vp2A3Ip& z);{i(@sYa}K7iKDqsW#O!4#TZDupC>zjd1ywDA-_E8%#Xl2w(4W=+DV@?&jhM&2b< zto4cM{kFCD@!RqFWx~82p;^Y+IYz_0g@ZXouG9}or;xEI zq9zVDFwaL!|04)x)#Sl_4mMOWC4wnGD_HYU3~r`re18n0@GYR*ckCq4xJ@V8f+bWQ z|5_1BW|n{5cVf@0bT7kZaAFNZ(!-d3U(9df;g5zov%cy1{n3pQssdSn6x0-GtB@7I zrz1UaOdt@R#r*9)qlz322}jLIBg=n7RNQLkVv6tSqrMg$m3T3?pAj z*&F6w|0dNhM?1&>dtsusoJD25n5a1}L6l*_E8SRzh_SP)8-E{x5zVp#Dgg?!#~rI2 zY1nC147~=@sgu0dOwQMqKBjc_-3wVYOySFuFI9yrShu*y2^u^h$r?#WOJY&3P@W5W zcsQzmY--pJDZoZCI3qymaT^*-2>t6ZyT6#r07qQ= z^IG^?i(x`nuBf*%a2&6{@Pq>ntCY>Pu2gVhxRx@dR{$YY$jqX>meSsmiw=7h%``yc z){T%gVruj3r`=M7*tPm)H$jvHp3LFT(^$C09l-2BtJWu*%B>KelOv{#MwM0PvU#qI zhLHp5Nz+3&_~Lp%6{QCy!J0+_WVAY=?9eqaP1Uut6MBPNoxv78SG4Q41)MHz%2UO$ z(y`j5_;lq$$*n`KJHgKGSOZZuuB6=g?auhgu?An`>bo2g#lh4pCNbGG1(%$Sx|)go zDfuRzjSGu7F9uDo!fpT7fsFuLgF|1VUCeSX)hBQJw;~}0b@b)FH!drcdd|HSyh>f$ zCI$`T?{kI|rPfsjj$7N|s7A+l&}jEzBjIUC}nG z5+mHJeNySxYM_07Vp;Egki|*ZCnnLz|G`R&{E(~&5q0@p5J@O6|It1evYbP&hZ8|Ah8@VUxvi5(jOVrhP=g`lqnKbClxJJ zg3^hoOC?}CDw9Vo6559c{2Qq^iWjNj}!8KtvuPych4G*gZJQgU>ke*rt}*>v__`i{%h7O4i2K zoa+2?%5e7E@@Ysj)Oao0Lf=YHS+v!cZ^Y`2PL&Sa41RD~mUz91=sVk@UT5s>Q@;Yk z+BP;%snJuoFY6RwF$c(~|UWF-a@Y1MmB{Qy$vAPw{R5lE)s7SI>>-V9^uD<5( zVcme#)~w+I>ru zn1InSXnFB5QH8;SnuwUGPsAPJ??frIsQwK^`7i6Sa6>GNiAR&N#})eVRIkh|{1V#( zJph-{QRz{A;s7#T7)fe;0~wClR(h#gP~dRcI>E|2JH4-eahtp%*75KIe4kuyqJ#5z zE}52mCw8^yQrJU(<0~f$b%9+F2z4Wf=rm4~{3K%HQ?AgT9ikqUh#~--xg2 z3WI8x`<0D0rAPR>ReSTT%Bp5&%@aj;?V)42epQC!M`j4lSn-e$)Fru}i}Cp}3s>hP zoBg1tW0DkVSfx5YVuN5X)Nf_g{_x4PYtOIduRD%f^$`F<(oSdUvd#m$Q5xH7L~W~#Blu>4{-eaooVmz zv8ec_XVl3SJ)7SKzmNb8n&TzyMZ@@WFxN{!EtHG$-)V$@Vd`uorfz7QN_7A(O>60Q z3lTuetJJfs-|jLtoJ1Czj^F4qSzbtc5N!6^q?PSOdp}GPHhTJaSaBlK&WvBJ+GY*_ z!yTD4__X{+&MespK~Z1jJ!nUd-R@{svKeS;-Q3+^L2GNF<(3_C#oe)4)ha*sdsB`e zmMSnGluR+%(6!pHm?r&BOJcjG^!N`5W@;6Q^vJHr#$7=h}Uvedp&Sw||d( z-d_NCvu-H5Nlp*m1b%3zwgoh!O%JxeNkF3Sr1s*>@l_sy>LZCw9eZdEQ4gJ{aDNo; zpet&!OA7Un+(R%hVt47m_*ut`Cd$?CtS&F1c9SQcHMI9v8P83D!ogZA=!=4LtxqbI zdF&=XbxIv~l4x#%k$c|(MgBZPCJVSlldv+qL&REj8Gz@E#Rgr5eor@0>wT6sCNdC> zo3ea?!>*JkmR~eaXzY@m0DKT6?*^R z$JnO>%(*FeV=RcwY|;y08TD;^&47ljaq8T z+8*s)?>>pA$au}a)7>;bFhh3GnT22-IQgs@4i#9M$s|*R0B7iiT4BR|i;Jh?hpbJS zS@>B$d4#8u`~N8gjC9V{jmCG+N#*58j$JN}JjMl#%MV;K+D?Jq6R`B-CITgnhXwlH zQ>DFj$j^3`_pE6))ST^Co+OgPk+HjWyOo=h*bk$*LDHc=7{H*v%h0Y;({;DXj9u&ri z`?}`0W=SGZ#tY2>P-*(W*W@>-%RjAr^rj|`w6q?P0?Z+4UxRZqylUw?m|f**mN2xX zZRlmUPgHE$m9JYZeA~yNfIb`G6?4Pe?wl zcwNO+{f4PYanB3JOWsGraei_T1~<9w1_km=N?3Dm`Gp>cahUHGe(f6zHKwVs$nuRu zTHc5dGb!s_t=ev5KI8*M$ETYST80 zSXq0>%NCB7rst$dArX+3V)HVwdWh!Cl-XLfJKfYc) zlrp%L!P>?{a=5w-$B%7T8l7)GSj`Spv8<(Yp`HprL$7{dn%+C)Dy|~$Q|ii5q?Mng zR-`K(@hWU+?Aa6|6yd2s0wyCCB|g{xEy(dZQ{iv}8~h<}Xv!Q}f#v=*5S&QSj%vmW z+Dm%)RxvpD4Ttek=sJ(e+q@Vcw4f#6IIAW+2J7<9Fp!}QI}0`@LxV!ZhF1iXpVDTe z+^pkUVn}$ZCB(}l`Vf7K%gyUuinCvVmq=#T|BmOrGdeZfop|lW?nfphoSG)v+|1Pig^bDc`{qT= zr)}(f04NESgLnnw(csqY^MxU_#!2e}h?V03; zwOXk-*YVpe^sEn$iiWq84JFQNzm7ff1MP#}jB3>&j1<=>Jic~fshM?Gv$iiPxLB=R zC021feZ|XU+bMkia7fw0jipgm4|m3oWtdgmvX;`#sb0^qZAzfpc(`%%_$j2s-6Htz z0~N`_dfjPTm)uLT6Il>SkQPp2ts=`{kp?Gl)egf>1ZO1qrzR-gf`W<7*-wU#i$T4b z$#Ca8)_;&G3?hKeo~v5gR-1q~f)szuPaRwVp($z#!Z=rUd6np#Fn*L$$DmF;O&Zp( zYX$So$nY+;Vvf;tm`a7~XaD7g>>?z_5LZF@8xLuQWctdoUBwuq^P=eHdO=X&;tXxh=0xR{EG_w^+!;40qlZme7!X?nA!TdYHk2z z5(=<>$$X{i>TcfrUlKb14NtAXG^TdiV56kWyTImHetVb#ifuvzK@|JXO38l??2Ejo z1>$3^LJuLd1XRkrd#magEP|f*|7(l*dIV-6Hb&2>(7+|4OeFYgBc}f00HPk;_Qdav zVR$Yzu(jshp8DdrEpQ@6om%qg9w>`_ElG`>#UN+@X9DGGyHfwF#!Fa7;uN|*C6#y% z%&Ds_?dLS!Uw8aZ753FUKoo>y^eM?)+#Xj?b-00Sb;t1H%pcR8@9O`79MlTX|J;VC z8w57(UWeO)v6^YR?EkPUS*6uN;ZSz+R0GYT?&On!e_7O;uQ93~K+ zKMFtID`#fGJhlXJC~&v@WcKnR=}l<~O1!rC>cZdxArKge!!Ib>BoX4^Yt6-$yk z`=2A+?Bxo~MQ6O+mZfJTdsqnmAyae(g*6u1P*3XFX@LjIb2XGV+mH#Yc>9}7I$+VE zbU}Y6ozR2BX3iffE|4xm!UX;a)d_a!PJZ0CXUmrcQ+jp8>&fDgEb!q`i8-JLZTd}c z`D0WxF+QVC#^LNDWm)roS|z1p8GJ2M?0@txp@*BUP$_}`u*|D8ee$GX_Vv`g(hrbB zgyc7K?|^m61hn;iK(1Gi09^i<(acNxGx4zjY;CaY0(WEB2g8rY)A#0WI1XCM(BtITjhi1$@hS5$5WRAB$v-@cGv<43%o zo66@q^8+6Tip<(_B^Gb3P`V_maS2961z?)!wZ~nXRIm(s04t-wRG&jh@kgF8^QY|n z_O1q5zBf5gVUKzSqH%vWPvdNe8}xB`H57c8sy`hM7;rMVu`Kj>(cMh8d78nnu})66 zW!}z^$;Ok5zHQjv;_r-7KqayX05YL>BY+*;S1oU<2XP-rI2Xu+{#l5XF>wM$a|{>K zgCwRwHZtQyvl6(lwBXT1in|hrP#~t;Pu6tW#8*>rJJ`84Xu8!pKQRoZ=Bnb>G5?Jt zrlbJ2z7sROixS5ZspNTX6*B=5Q?~t+XF+*!VJYzW?c(iNceJV+r;yd${@w?NxuBY( zuy+Dv>Fak_uKH%Y1@$C&`1clTGwjg06gI+gP~;Y;9i4Wm>6kRLjH#{}$SAm$nHYSD z`#we=8pYsoHmtEr4m?k!!H4n4@A=Ymd8KP)XWuc20UdJuWUQ z%XdzYSZ9Bxr>lJiyrxGs(Z1bYAgQpQOlN6$**IVAYyIY=;q=E6`&^Gg@W^4eRk6v| ztFttJHLs)Dk2#*xGQlTl{Hc_?LbHql8YGZ~d5f$-aR-8|Trfv{LWlphwMxm4UIB%U zta7*ZO&7_<^$zDI!{KC1NAW{XWER_Eq8V~=(i)6zG7^#^HhsB5RY=;Z4znsc_~ezn zFFd)hMcxIb#r=B@!$6*N;s4B8^1Tq)@O4!t-4f6M6UzSU{C!26YuKRG%ugeohS7HH$)K41?_w27l*gR}XnSrm6^uy&3Mcqu$*>zw$3!q(Jsp)>}io`+_dw9dA#ns|F)YOVKPL1mSXGDBhP^UOp0>F|A2jd$upX~7M{M($@O*IXu)wVJ8OCJ^Qq+m3hJF#|6inA`-rcQyVVM)i664M*om4A6v?dAj#^g47lp50qw z7;rLYYI*eDbbjudS8?^r)q-5d`12eX;t213 zMw`M%3A@WOi-{pGI%>9V!K*t{P3iBeD0_I+pIw|3#<_fra7`bpyDy(=IYjx{MxcUo z+^Nbo{$&pD#%>`SGqj!zjMl2%=Qt}Z_}X~(El+V_Ay&^OrP+9*>#-k+4e{x&U)dcK z{(&yh%Vw2^Tjt-U>=<+}m*+{b- zE(`Oc%I-5nGz^+OATk0+54~`DGrVkr?ZdV&ff z)I_k$M7#{YKO#Z}w-O&ay-!Z>?m0ZI1nf-Dk|=>qrkw^LoC1c?2JoNwJMACcZ8E)<)0!{gKzZ+$?a4ir8JBhVF7#=Z3l@!JQLS8#0sU zAJ}a_qY-?Ftrrs)m#%Ux9PqJclIOJSuXvtlLr-uVoK6(~cWMfJ?mv&A#OSCeHBOG# z2gK3H5Q7oy7Pr01?=o7fHU#r?3Y9HUt=Ft?&!a&{>w;uHD=fd?Hlb!wfIIkj_Y~%F7ky0W4J!5MiD4MHNQK(Y>fBQ<62292=->DIMg@d5t zHfjoz{9=D0zd9rP4gQm|t+VH`%qh(uc~uT6SQam!Q&97Vh@^}zfY@4C~Pjl z-1bTPHH_ws7ZhuKY0=$MIcqMbL*jExmy43T4Lsayto5pA$9m$`IK2nN9oLLSi-*2_ z_A=`{P?ww{oj*6b+cno)OO)~4qN>M-L$HR5fQ2*?IH%l7-*3L-mFsWUb-dKN zo-LF`)Mchj?WD*?MguX}V4wQ?hXl`0EDxSD`(szDIQP{TjA83X=uP{;l#u7DnOu9| zMsal+n41Nfnr12SW4_NjlUBlbXPRuno-Y#i5RE}y{S-ZC8|j3tv*hDbhtbm`E6-HBumx+=bLRN++ntzjPTl4KCUIW1!;{gPed#l+P| zo?nj4?<@;6+w+wj-`9nZ#6h<%CypAo-W)jg8cJ$XXzL5?VOK>jTX~aXYj>Hkl_!jE zSwFlu`?8mU<|1ApBYyjA4QqwFAlC_z`_HN}#{UM8xpf@suUf!f5)(~KSlkDEJoOho zF8^uB1P_B&D$P_O4xa)vofF8;w%MT^XS)G*Cc6Vct+ShF=k|o`72(@+8CX^ z3~?c>UTurJ@bm{1LFJnz=h$=j?37890f>}nhYP(zLH-M$4guAijcZp|-N^LWx+n4o z+lDzPhvlP2CmwCjG@~42Q1&z3?v9&Bcgg{_RL<8uCZZW;mPr4_+o$x)(bj|cEw&zn zYTT+OhR)zclTNd*Q`+`;Wb{plGS8v?W^Dos)m1$5zkk_Nh!nmi2}C7Ea)hy{t^jtn znn{oVpjuzu4NP7r&;x80`|6Azp5NNwr}1mp=U_m*v%YuXdMR8Uvl+xx^_~1ipW#+I zt!XZStk>tEP)KY%TsmY%esO)}Y0~BPUA~&jhy%h(^vBy5E(1lf4F`6_hllZbQX>Jx z*(2ECgg#szXM79^qPy$yETG_O+|T(NI#F^k`Pu0>JilH_lvi=NnJ{jG7xb|RG9R0T z!}#sA>SlR+RaNa`A`Y-4{Cdxy#mk+aV$IKXScI}49Hb8vnELS5u<3Rcyqte4;`x+2 z=jfBx#%z1S%Lr-u{zuBmI77{5_|j5g@gEwUD($))v>%#=ix@mznLyMF?qw85II&!K z!y7NZ7|qX0GiBV&Cd3Lk-${;B|w3g_li zlpY#jr5JiE5_bH9O*X=`Z%UnlkzZ!>XyYN2 zSz^p~v>|(Eptl*1nPsU=kMmt{4rd9|tdDw9X*(mqhF&}a+l)Zf#IEAS;~Rukw`};|0suQ_lfWMl9+s-5yjuL58$ky(5oksMfdJDP&(q?_b^zm0K@10 z7sKb5OUX=jaJrV?2~c@!EE~KKKDAd&!D=>9^+*^SaOYkFj8r5trfT$4ehW)?3l~bX>V--xAN^Iz@F$t_sC+RGjwV5 zqm~{_=5ou#VG3_BK@A4QOOjsu?zf`W?zgB|Rn1P*CwVErhX}MouSaZ=LuaQY2RMpe z+(OAnDlbjsaypc03679hx|YK!mjKj4+_u{xH{>ygU-M4N^iDrGIhMSZk_b=6sEOBO z0amQPK)3AcG7ir4{Cf($MtUfeJO|eG^FReVdcU@+2>=%7N8T?MN3E-ZrBj%#?N%yo zyA%U99$J#eyPI#=7QZ)J^LZ61#jUo<#%mPf5s_tY9LyT_#=7hZ%8HNO8h;hF**yZ$ z|5%?=tuPERu2Ln(Oyf_c%Uza*nGOvk552 zJ3h1aNem+%bLI~HTd;w=3pomaOJV-xRiUvm{wm5j5<+4dNRlxBs(H{`Au6%gf;wsc z$wj<^c^~jy1xJj(F_OU6=2rR70fFuFe?qH ztNFcVe7Yh#+i{Q>5($|;{lF{TE9Lr3_O}%VB$MN)YSH#DcZnU{$)mO3B?d;Hf?COd zRfY5(OD<+Vh2V|b`YEVcJy2OWWd=5F7$ItU3LvgIYG-zK4(B7CABK=3By}`@rA}km z#P2GpzQVNYx2=EA9Z=2U5x3(LKuyX5DTs5NZ2K041RQTE11gWqJ9=M3tI}3G#}%X* zHDX7dB2_G!vA^#eO6g6ycp1NE<(Bx1Gvb%5eA$x#JNmfRj z>q=5SdB6m)Jq50H0tY34!Y{#A|2H;qGX6_kz<(BXP9X!jbPeWmMvaGmc;p+Yu^@GloN8afzb1#=p$q)ip>f$zt{4t|`cEjNxs;c1oI~<} zhv3%qawr6>!#QduR4Tvja4L~*O~h}!dq#=lQzkc|&=FV1v3x!e{nHN+sl#u#OCm=v zw;N~lhe#DA+R7*)u^W*ZAQNKuw=T?nAmI_TlY5;!@%=O0n`h?E{%Ld~~%0=et zT^4P*GntfhJh3)EVNNuv!hvio{xwyqWW3z0*SCa;wyc5rO(mIqU`LXF%bNUGGUVu3 zcXj2qvC;mr@mgqc2x!D3jc96ZvP=$TAOlC=pJhC$pC6vpCnu~@VAN#Oc#UKv%eeQ= zDakU**>%Z+G`*(rA|bWWQr5PG-A(mGNYDSJa6lEwT4!w6;70_-)oP#)ul%TE!Yn|N zbz&P6J`p^+9zyBPK)`^9p%H{SL@rV&70GK3lM>Wn?p{iV9C0iY6cH4IHGOqP`CM4x z=+m*Xy|^YN5fkp3c7eF=83>H?Gg{ZaaZLSjKC&OCv|8<8-yX=3`En zOG!rVfRBfv2*~__!?trIStT~I5BM5?2BuV~Yg~FDQgDVg>bwHE*asC40O?}I-f~jt z9L$WV$*8Q-Pp%g@#@3LL#2=|HCzMh#fk@C-YZP=^uEf9>J>3Se4Sbr(EMHt*amM+P z`uoSzukT@zxqwbud-xzCf=jCr2bA7R)E!-#PQaW`-LLQ8mM@e#zhCi+`dK{C=_z09 zQoO(lt@m#J8ZZj`#BIFEP>6VJ7(4Oq=dz^8^R41cqHGJS2Z?;3R4c_!38iE|*W-;OZ%sXsLq#&w1|dToO(0b-c0^p>^+1v5q@2cOs3g{s3UzJU1E(cp6K zG`eJB@jWJBEp;0n1vz<(+2Iu6&itSNRP%bc7n*VW?8?k-T<>su7-|%2*_RB*+Cc z10FDP%8V*mRF!ztI1j+@pMK;rik1OkvhTk-e+)R2d+K}x=B807T+a!BXi}!V552m- zXmWYd`Mdx@Wyrpis`BT(+gFmQld_5U|99ORAO|ee0Cm8@ePC;hB!8mDUR1{hYU!o> zz;C7dsd&|pl0;XDyl9j#03w)!KL(mMIsJ2X+|LJPz`ee#76{}_>sREZLSB;)b_WBc z7-;XexBz}c{Z#Mi$Y9j%pAg;*ADAMc?ObrFFp>vG>ZvG*4S;>xVUfOMDBAE#y~Yd*?MvE+tyvdn6y2BY_GPcoXAq252xQinQ?W$HAw; zirx(WH+UQcwo-P3+$cga@;7{h?`SWjh1W+dACuOR@c4B25sep@C z1rlUsA{}@YWpmHF(K0A9K>l;Y?x(6^0*Nz{{>AN-3IGULg&YGZ?fw$TP&#;X1=AhS z;PvB|NXj75z4Fl~DDo1<1&jd(_D_L+I#}hdhNlG5FO)PNzNX3m-Hr<&+<%ANu|5Il zhVtYYQl2=G8;ARI<2aC%q4-G|qz(Q+*ZhB2`v0)>f2+O!!_xo1)6y%#^@0CY3-E7I z$^VNQ8^;@EJot0vxk;46*Chs*T$gjtR>bXvg^t)Nv{nQ>ZxmYn$sNHMm!wO6+Pgq_ zT5T6YDIbS-#($mX0BHkfc^!(|X};Qg3D;v^t@R$%ZEr`7V(E(hwAYg);$ zRvkdWO)KHaCCzc+Q}j;jz;wCc^Bocb8fga?YCa)_WLg@D1(V>S^AMHT<#w&pCU|K- zVy$%~wpAK);JxA_v0-1_vi%7UMoCBc_-vN7(Ov24ai5ctg*#~+$lk9DhTAzU)?6oc;&D%9i2vt|XWKH^bVSRH>Iu;6r7gL;_M~6JK zWU<0*(+jZrP#2>dSx+Cr$>#b98z_atqke6T-XiTJYl$vHfsVP~N3s*q{Nwt)FmrVfgh#(E7K9knbLl z3>V>d#G&O<8hYmFOpcMj_cg>E{oL+#1o0g^uH6fVMQ?r|H}Lg#po4ZlJn*ewi`lLX zPvPDZI8By$Y~FHVoJ?=1rn2e;vt1w20Fn^iJ9*mht(pXVF?g%SG6lg~ld;z=b?Bm* zQIib_848N9r^Q1f)gWQcA{G7pJ4d;XO`YK{WF8vRSVsAS8tr-sZ(6*@ql83g*M0_b zLOh!IK(<5C*=g957J<5_l3Boglgiv|FNzU)heUeZ{TRX^XhlQWJ1EQWMyDC z6;5(Isb%jbga!5+TOHhQj+joj-23M8v0kkhvoB@Mt!t79F?unlz*sc?{ndf>ED0Y4 zA{{Q#>9A|b*u6?4b9$Wt?;Z(F+fJCt=n65zlOh4t@7zB!ZpP;3Gu+=;G6=Rdq! ztoF%_l8cCWw~G>Zun-^lj8eNHMdHI4Q~Bd!ExY)YGOGrTZSNwaKGqTY&?poI*BTNN zq$e0gHBApf{eW~8(4LAT{eai);8fuD;x$*o#QJYp+>U;iWxkrXy6`@I{v<^*c76Dc zpoyTS4z1QnmqJS_8mU(6q#onV_zwZBpBKv2IE!pJJUT1a`$7XLwd*e5G}c-~o7_`p z<$4)X#uiOR{+K9#yyc@03UJQaIRx#_4Q2%{^+LU?T{0S-o)4&B3cUQ3S(>coh1V#r zt*5g+UBg87{8sk695KALAzZ}^yjJoA5h(E|hlevBMQp9&cKa1iTNbR9r9ZnrkRqYL zcyec;E0ILE-R{+%J<+vy@QZk~zzR$>o0Gj)xB~^Yfs-4LAV%yRgYMBJpcLfW1OH|t zp>mSK_6(tiZd=M8p4-=hf`nCYd+_l|2>N!En-A8myb{iE%T-(8U^M04b6GDd#me@( zmk5!0Di_h_0B<-sX1{ehP*e4JnYvklr2G)tuJY1aoW3O??BS-d^$n9Rqm!Y=-+U)G zo7khS9sBI$hoiEQen(^P&ldG;r|H(KvLx+gOBfy9fM|O~-Jdjy@HLxLitZvkcA9R3 zVUE&ItN)N>{IHKTs(M>kvD(wCOYKfc^OCGzNjIqB7^d~P?9oqA=|>;E<#c-9>N7ES zt!<&6K$m)SZvf9A*~8i1EgJjN8~f%jGh@L%+xl(iR&Npe4qH(b*V%NAbp!bzYc_!q z^NN^zZervP4@UIBUD38+b^kywxz(Y#lh(-hw+)@W+2vk5G|2y6AFaP*! zzS>KkYE5FH=W1t(OxHUvIO=!uX;MrslLR+1Th?{k>Ns!O5g2y_Mrx1ioS5Db+*|1* z*KV!pjj`gdJ>Y%Rw5t~NY`}fGf6%e$os?HQIzyrEyjoIUVPb^!V6{%nn>)wOr&qPT z$z_YlUQ(yReS%B~2U$hS*5;0j$>V0lRCuq_JIPIK>E&QB0Xr$=`^iRMI{Vrw)lO$JiDTaPRK1C!(6i48>ey{* zRXnOMHadpb4@JD6wzv;xJ0$Ck<|a-qT0ls@#MSa_K2q?x^e;>P5wg8LUwTT-Q7*D7 zCN2xzyHmd$*E0Z@zGgG|1(-*lX;y9DqnFQ&P?lSG_jjB10csjyF&|pnB|$fx?pJjb zL>Q%tNtdUP`y_M6NZ*4&!Ut5(Pp~MMCb(3Wtma8&&X0JL90uuY@rx%Fm)ak5K#AJg zD%syu58s%$Ds8dd!aSamR@{+Eua$RYa%P~ZlsqL3<{lp=G+tx)VymO%!~{lBPH+ zHDMl2oRzSSpQogp1a5lXu(R-PhIRRgMmG4C1->O*F@xZY$cLYXRiS}LA<ZJVn_2kVvHm8Z^S+uM)txy4;{>q9d>xa}X_811TP zd}<@G=M!-{QgP!j@V=;nu*ZUR0PTB`r+}}-C2G6-ni0d8k9gZkwr+Q=Hzifhn?!N9 z8sQOs=*lg9U}cQb%5U|^5AlGy)goyUK-5lMpy(GQrAq1BOWZWKZn1x%ZpnI#(0^wD zm-yyYdN0E7ezvmjOnErBh8=%83tM>!ada!J7pMJXB+zRdjQsDY^@?g=nje>FRnlYQ z=aw2NGsYLFb!7Smhu@2ijKYh^=Wr#7`)I~gmj5g;)Y~LAsCrYeqS-zMkF41AfkkS9 z*2eRa@y*T6=_kH@@a(RG3xmUZB>w&g&G=ONjJHsY{dmsj_ZAnsPT_Joln}#p-nY|1 zf=;?qAd7U!2uEK9S3X?;azH((~mkZ8-V)n zf^&4Tv($1%U6;y4ch+}UBo=k^s5?SNv5N=pM`)_nY{aXFD7IIeQQEA3;C=0t2Sjw| z=y8xWugjY66Af4ppGOv*T+q#zvxAz><`htvg@(8mEGpBF(4~TVWNz!IY`zv1>yzi1 zMBVtu`w!Z+45qhk>c!8@Hfq9KzmbAdzh2c^QVQ4SR87u`7XbZ5g%T?2@|ZKBS1cxY zd3OW7dfmmdao|Tvj<#rhsiZ(HLVC}0qDkWW>Ac&7+TJ)79c+!o2-SjMRPyX@W0{rJ zO0vPWZi~)Y(`fDP=^E5YLsp^RV}mfw*Dh%5PIC~wR@jd`TO0kN7q#+7w}iOu(j1tl zcJ*dqu*OQy5V=VYCHRFApBi)cgQBO%NL9hf-Dp`9EM%lg1SeGu?J^yMw{Y=lb@0E^ z9=y>=(`qDxG_6Mr1Wn-hp96mpHZntDJ>Tta8PU5Rh(>m8$;`YjwzvcqvRO_Ax$oF1 zocmb&)!Gp>SNK8?9?mxG1s?mqAs3+~U(*eBuRBQbSX*#@?f_ffN=|2Fi&)X!5JSL8 zn&kVAcduZ#g(x{M@8wR2sx>vb4u7>IlPK)BO4Ym}Bc5)&S~{zHcwyA`;0~4 z|0S+#-E)mO<``p+{N2~iYj(>#enES9-6{yIk=!JyO=3TPPAj)}$nhROi@yNxW{*Lh zB=q|10wSS)9-6Nbzt9a9+sg2t&2LaB+l9_H?d_Gt8k=YibnT#+4b>I=P)2t;6@pNwDca4vDMJk4MXuLJ1 zzJs$))f#i?WGsE9crv%V{9dV1y9JHJtUrGCM0pg-pj><_yV-ouJ{PB?$YM4zSaD6< zR8)l1?M>?0FsSyh+*Zk_*=a}tFY!SZI!*L3h=}Z6u1C|D-*-A)Y>E%(WM13HqnW(ruTAH@-Zht%vU^2>Kk|~s zp(N6K#L@7{p~|tP`$9J_(SzMj^$+j&DYyG^&@?$jDK1X9LT)csz+2$9x{C&8p2g;g zA0HHT%(HqF&}eVM+2OC~$-{l#vmfA;F3UyD))jM&wWv$o=BLtOT-+&ew;wPqz;gBwb^*B>|`yNX76sZt=EVbIXDNYMM}Y^lG*+6zk% z$>pZ7{UzHIxJw?2$mh;P$!!7@UOJ6^Q=Qtm6i2tMyjbn~K5b=Fg^1PTIM-h;ha%H_ z+|Vr$R~~yC{NO^nEWWi9b#VA})8TQ`qjZH(SF`WPm2g z$9JO@pz7Bjf`7idr|x^~^B&XrsRqQj0Kw++^^yd{d4K@z;Q@=+!mR;w49*_Sv8|T< zDPnU?IJ~c#or&z4Wc+(-=jHwsRv4Rh;W44yN7&7oVfJH5u#BBWIBqa_rNm!Yjy0tA5@dY9v;RAi>*`oQ+5_xZTBw zb7R$|nrMwlKATHDkWf2R9kjC9^r(Sx=-rN3@1smSciMybG3142k<$k=Ce^Ys}w#bofxxE(vabZ zJ%$IG^>Ti$!E@2R>PZ{=)*ZeNw4RFnczP7gkN+`m@tG81(ZR%7>bLlML*)(;#?I`H z1~{J}1nofB7`jQc$xiwf=VZMz8XR?MI3m25@yJK50v@UZ@7@PFc}#P-TgL|PoZjbC z##r#=?D-0WMn*~R#SG&Uy~8~5m}gAiy7~uWm_1N!zW+Kp^k!DSyxyE-=VWV43sO?F zimLO{@#?XbH@UQ%B)8&1Z6OQkgupv|yA5JwbCT6Hmk>V>Z?!!^kC)*=-)C8PU4V%l zx*h*a8V^BgRxZVsz~twRi~Wn$+o{!7n^t=a+c)x40+iY@Z}}H(Kh>`bpJt$yI^d-3 zrB%ofK2?aE&KH%^X8MTPd7M4PT}gkGJ&+JSD7mlmR0Yf6SVr{RGnqDZ-iZE?W9>Bs z=28LIGDi26CjlyTsa&Y#b62UzA#q5bZN=e4r*w{_(yG#j`r4((MRBcrC ztXpj(EY@edbH0~eI;Yuc!<3a-lNbf^IJ$nE-)>C<>6zvW>ibTK!DQ_7d!`pCY zH`;_b{XeQMD%+{>md}v(m<9<>2bC~9?L*BLwIZ$HrXj%le2F1uJ9Kp&5arpDp!Z%| zc3-$4KR0MrAiZA+V{er-v8vmYh%MxW$T)IkGcTJ{4eJ*@qUKy_>0J`L)uaW zSSegpNZtC3!wJe-QHo#y@MJ@Nxaay)vRw)z1PWmTYW=Qn3SSdOs%~SGz%JQVgEKE z+YOn{G>2O=sT#PnErLIL_8T`go-kQ3r5$Zdx)^$Ln4lmXKYkti=?zIU(PX11pU~;% zcp|MfYy)9aO_c8O_LfiIq}z?4rx&?0#$JeHYT;n9ns#oM;4Exzyw~#{Q%cxoCfYCf z5Td0AQlbm3blzSIoLjWv$+SAkxAeuo5hmSVO(r}Z`0B@#*j&%B~?-fHe{5w~d<*oMa**Udb zgSEYmZj(0B32Dte8OY*Z-v4_~SpBVJximSXkT=7gLzU&!iy;MSry`oKm8SSxdV z-W&ZxiTFX%gy#)zmr4fF(~A}NW6soIWYe9!-yKb(QfgB%8GaO0MQQ<1vJJepP!{G?OQ%=oDiKEYM8xNs2mWcn*zSFiS_M`)4|FctUK77k5W*24`Vs z;NN^tAJihBl~XkqQ+HlpeHn*Yd3q+Pt*PrQ+^}TAbFTSRN3i?dhfF-(tD1Eib5j9O ziq>e&eBS7=(|t3s9Dn+AQw5LHN{yP*$Hh!qLg*Jhr=aHEviCGYSm+!4c-BM{k~-VZ zZpNh}ai(7fK+YnPfv0xl7Lf}>dQ-nZszs0OQH=77+u==0>@G@$so13xCM4|)!> zWZDgW_FFmnctwyB<^Q8>fWso@qiaR@)r2k9WZD6-MsLT1CbP-hZe>pNGa5O_RXpltc zj+8lpp+lWMm^;A;RH@Grc%Gr3(k(D=Bvujp@Rmi<0aSzw#d6JCRAPgh!}27)xXo5~ zb?-ZeYDPXEn%*pb`#75)d;6y1Wx}uZ)d$&i)X21KVOtO)L6y%p4qay&o!kLSHf@jR zv?Gl~A-~ibwE5?CE|yeg$6PgtPcTC(=H~Z9nfQL% z7%}`4gcT=|4BMZlvWTZoF$`r=uQy?Yll-xR7pX4o3cjCwOdJTqn|xz)IDGIR)cm1c z*r$C~-los{NZ4<4@&e8*{$?{J=;h_zm49YT zxZc&$4IJMU%u2m##2)-Jp&S*zeRD<0pt<>?kD;dDRFI^kVC||~kCW>74c6`d%AD}r z1YAW5CV(gco5mX{-W=LyLidNNG_V>kPBSA)32EpKY+pY1l>s9TU!)Z!6KT+X$eQy$ z8-pe^`ZQwRvN(()n5jY(ucne#VX>;w)G969TdvBlji!y6z6tAF5;v5%@w6ugItSWy zy|+gpN3{9m#ogzCNsRWD#;aDr?TQFaU5VtWtei`5C}+cGU?Uji{c>22?QE+84kK)D z#y7|?l%O%igWZzFZMS9FAlK_^MKoKHV&YsY zf+^Jads03xFzV`&8Va9K`D z5L-SNnid3qC4c$WTiOwM`+;TelSrjweGg^An3YQ^?v*}y8+=70UVq-8TJi^QBomjK z#aNVOI_wh6Qu6GN3{vr3%(T3At*o#apVpn66YOTPbRUMJMjO2|>%(VUe1IqenFzA5 z!P-g7&O%ks12?$p;~p_mmtRS*nQa4{-+nVe)@D;q$jK7Cx+k|MOJ6Caj$ZeMucM|< zi-jEZLIM(LxR~Z#a9{(BK-&S@+9^m6OI8_&8xc3%aBcsR#aA*SsF+(ivYQOe>8^io zOFW>T_*7bB^+gI+qRY)FL91!LIKTe*=zmipV6Ujt?Y2R==VI{YOk`33amYq z<|JTyRM|>ypQKZ~2(eg0vV3px&7+FGhiIFfT%iDAX^dSNuRbt(UzbN^z)p|3p5w*C z@u%6ZpFq<(!htp6$$Q;IhUXW%KK-jaQLj-CTOeKBi9vm?GBfZ>FfDs-F~M(`>bI5~ z#KfL!mF3MltH&DA(0Wt~hT~yNy32%ee7*R3(h-JeWu zqTF+lDvlhA&`dML(zajZl_slr{aH6XT5L$mx2LHV0gu~`qKppl7F8B_mbyrr?-?>* zo(TRTG<1^Kf8uP5072jEN;M1|G**K_TBb~Ul@Q{9QiF6fHNpL8+2*n;STAgK!p(4Q zn=(1(BI%u@)~`O8c$DW=HpB+kBzJUqdmksT3q5p5)QjJ*&3k=pTO6Tu)mw)WQ6Dt5gN$n#qhfN71 zdk$Yua;NjhDXpJx^0SKvMO8kpOFRhAp+1HYS|?C8M!(sYhs-=#)K)X@ORp<$>BDg( z$?$MG_q*=@fSRrOQDv9O>z~fPMO#lcgbL5T9X9{=Vcx|)x(i7|xkxw!BenJ@qgk(? z<_*Zx`{dxB=zEZb3<~t#Gt84oP)bCEC*W;uFsio2-|p}<6#dlc-=H^&8&l^hq>5}< zH-g+k38o!)L1ZJg>hkIxY>V8-{-iabTNI z2_}t!peG;KHMFE6qqW#v=?7IzkAS+`Fdo&tYgY7v-lX>38Rn8|8KMcFRv-S4corO# zHda5!;LFQ63wkx=kD?XU>WSs3aV$-TA6>d{d+!}qNLC(>QR3|h(DX{= z;2lM0Wp4E?lwK`lQxsLJri%FIY|*#;vlk#p6(N1E)OLb@NlvA_vY}8}8f4f#R4#DWo437X~7OCCDtyv?_0I0E!Y^UO1B26;4K*I7AVgwQ)tSx zSj(lxE3&T^kOy3UbPQYLlZVC7gR#o}K*8njC1|i$i3o8gY$jeFww7L-(K?BJxF^#w zsp}NQo7064TEz6|%nW-+2fk5iRbg*>Eqbzv@3G#jz2Fc*m~lm%Xlbom)CCiT?WQqc zP^D!to(6G7lN7Og{X;M)`+p=oz^BT10-B|QAJS+QiGr4}6;Ss5kI7y-^uJodK$ll< zl2GSN)?o9C#EPQ~%YJSqTBKmO?b3M3h7Yl^(za#=QLT@!5*Gt|lz4k@Gvb|Sb?B`i zj2>%YGtD02s|BKj%~orNTr_yNbG8i?*GYM)!sIyW07W@IirK5K7ul0NknjrHa%^Jg zNSn)wczGPSkd@~=IqXxRx3(u-$$j*Jh(8(PvMvQX&??yc{FTPmq1YbC9`qZcDMfJ& zBsQs%B|nOb_SUioiF<5N@-b8IY6zB+3e%44L%`~Ft>E9OW3KbvP19`^HBP8xd(Gyd z7b3x@pXVxxat+ZUh@K_iN3nRb~mJh`Z}qhn(Drk@Mp z^h;&70x6cI*N9w265r{Rzj{sVeL&;fXW#OnJ}g&Bt5JJ8+Ykedh*JiVRSbu;UdMhP zJgJdGNi1`l2zqF9Gb1N}@Rn3c)El$-;-WPa)MP;;q<*<_XV@S|Q1emIgXA6PGtWf=E$g8rC;ic9JCSr=T2) z^3YpK6#0}ib`etCEKnnru-2&dbTF~Zcf$6lPS~x#{uGCRcW|}R$pks>he(C7_8}~m zx=56)3S^!zuiuMtAorX&^cNEZyVqe6&~C(&<)D1=F&PhucYJn6oWJFD_A^PTH`7K( z2bLF!?|0lC>+@^Tawx_rlsr5@r8+VdA@SV-Omtd-icKH#qE)OJ553VF-N59h-b=SP z?{%5+dinN{U|kq{_(8F9GyQ9R@H=y2;i1vo3+D;9YhAQGW>9&jYkxdY()P5R=t&%0 z4Q|n#VXFYj->xGrCy!E8o(aXoc2(Wcvb_i0m|z*kgo*N6{Exwta-^1t#kkwKZTQ3g zykKtUEY2|nbZen^OK-gAXDbOZ8E7^9+ou8zP~PRB5dK4FBha8rLbp8)AIa$z@O)7<#kbTe`X)Q1PiumU$qF1ag3` zVTy8c-r_np+@`$lbB~)bxFofFJb87^>x(qC?YHNa-gDO%6m3P9gGP7*<;c?>#aJ#O zdwvo+ClyIt_Jne#t(>ST^huyCg15;%>rQ0Mj9z$u3F8Xd?ADgeF6Cagp2eELc67Sq)PKsJxibA{wg&X~ z$36`0haEj(gDJfVemTwMC=E3U@jHjHwGMr1ytK})Umvz(>ZBpXUHR*omcDmD>55+N zwX|gFEq%}E=K9qu;{54?^W%XQqoc9mtL+90*a4-jF8b8j8)A&m=g#^z;`E2dGM$(1 zz45IdAp&digLA}_lP1cliSTs)QD&*C8e-(fBauXJ!eL!cCdE+}?vXhg%r&kA zG+9Aw6j%39k8Bedd7?G-Qsm^vca?n`6cs>Va~S${Wz?eKsq|!C^^hr&#Zz6)ErMy zm9lj#LjmwXAEZ^Cj;m|E+R9v-{&T~@Pdu}e3aaG*gFHAJk|u_6ru%EV(r+p=B+5f! z7xu*3mAKT{;0MI@2oOIoZ=S546g_Mr+Dgnxu4a^X73xN6c1Vj#d4kBw8vRls8&TgC z(Kwf4yYn;T(jkhl=_0=1$q4^DN0*pjvS&h;qtXtCSo}Ss;%l7Si_NZqZ_U*z-ujkd zp)TkXgHdW6|7dH|@ZH_Cu03_Hr@&i$%A_2!{QdFI(fZq&!?20NxkIMI9J-?9y!{{t zT?CoQ1U8%>+(+eT(&mo87L_V5*`81APo~UvH@3*BKP7j6v&+(s+3*CIUi&lRahmoM zwn|y$i;h5kYvG@Vg#aD~=j8CnQ+p*Bd096fhTB-1Z=o=M;` z?&ikpX`O+_7yC1k*gU+e?%GEF{Q-QLg5JHr_L~<%8Nk&)n#!-+1^I)4S50Vyvc!R_ zEKF@UKQq}MpvQ4+FN@T3=6bDj*s0hXd8z3y~4=4@}vi^YA zeH?Q;^iBKSk4$nBL#nX?N{1{4n=lq~uSL+wRqK53V$Sr{1v&cR0?o#?$2;SpVpxLZ zBSJALJNI!TE|>z&vlB3B5M{!@aM@CJgA9W#g4| zXckG5+vGr71Y1&I&W2cRLGLx5c>%(@v;Hl&RhAB<_rrVY2tJ%q4#9lWLBCaqKv{xO`%iefSw=aetTA-mvS(GMm9Vd3=ul&~#w2fvH0@L$*ur zt?So#N#qGN5}+mhL-C6knZdp~@p=MStZfM1_TNPjn$dqqBZ6GBwohpz-gaad9s$~k z&#Lia>R)Mt(x>g`H>)8k*r6PX^oKcT{+`LLiUPKCya!2#uU^+Sc}X)JOSnA5RPYCd z)SXk?|Sje;>g$NF)> z#WC=8XO@sNt+_t9sieV}!;!k8nl^H*(b!ZC1ezFInP&3bwt^`#R-M)a-pYP9y!C

*%(LM4hl*`@QVvS2Xb0rzWf^CY_T|m|N=rU)*4^p_kZvgxlMl ziCY8|mQ{29ZQoR72U=To8r6M4z_N>X?4}X+<{t2`EC_W|9z=pXz#NO5y@ZEy2TFZfia1D3x`Wn1VXDzberbxv=xy zaPKF<)U@t&LkNF3R)ov@nopkRG)8wizJ1mEWOa&d%Fw)TcC-g#zPUFwE-A$9<_lfD z(7O-VRF&cIV@a(SIM1q{w!eR!f#*rmU0pqo*fyyB*;dY`+v)|{>{r}H`HBHB*gdxJ z)})#%7nO>1kmte=Nz0$~TM|<6IAu0`SaPy8^kJr=33^Dt(nc-vQL#Y8w9BB0GYWE= zcrie~b)!9*EnR(o9%s-=4>wd{zi%Qh@wU%Qp`_!;dIm4D_xY&|^&Rlir98jQ54N^V zHaaNikvAP{LxW4Yqr$rGjH52{-5Rn%}QGp*(IR=BAnR4lR2fA~D|wZdwHYjL`2r;FuK}G8%X>Gn6?|Q}9|KNM?_R6mhxXv`7w1y?u`hAFj z7SO#h)NW(B=*+hGrL*#@A0b5?LRP4g{!Cm|C%t2rg6l+UJ}0_(;<-)i#;yT1)@Knq z-g5vVvWiDdCB8?*fis{+_m`^?Njl$D!^xfR0D6))e(lA(dDgN@yYWm$XQ z^1QNFk5D;8>~e6_fg)nDwx@~S7ZtS5l&?+}#MGi;Nm89D^Op)I`p$AV<}2JDA>p7# zMJfg^Xmi8t^VPY2#$rpefu#+g-O zha@L@6=OXlgwXE8-ZHB+0hvkdYyCENYcreoTf*V0nyG25JNJo)0A1kpaLSy#^MAxfIJ^}PPiz*|7kdTIetNue>J#F?YhbU>T z3Gf&`OM)c;q`-m@K<_!5*Gi^%gfFRhy&tYqA` z%gB)4-cYAykruW+4BldAJzsf!FQ@FWWN0gRz1?$bz(zgT?O7WYTNUC<`QZ6)J8wR^ zI3Z(w7&nXtc1dUMfU}oMDJBb1qZA>BK;sN&!=O-2G5dO);V0>H{~S5rj>`-(3HXo6 zDDd~+3F@)Qdu%iIC97+!gpl&cOg6{-QTGaCHALu>sZ0nAV%Fmo{uD)e5q13CI^gxu4YbavVT%zpClNF8ADfGUdY3S|62*w?=mGK5R702@lVcNoLL;xe^_J+{18| z-oaycYTAwQFkXO5Jw@&2_|Vcy3Q6GxDw(_f!M+eJ8B5XqvQw~vVV^OMY1M2)& z3>ht>kZifn^2-Yn_Gh#&iU#cnJV__d8o5ug{LXUSN%3@@$oyficQtxhR=t^LocO;b z4rE3@2?j9{Yskd+RD%2s1md?GinFSsw6rY6G;AB2lBOKl35;PBHj>EC3BoZ*}Ru~YMo4(9LD^gZXXy9A03s%QTa zJB2U{RfrxviF%v^st_L)p+K~N3BPkk4N?YoEj4XoFzo10Y~L$`5Ovo%gKv4tcQdX0 z@;dMrtb2k(%qXR*jsd(?r>|+x9MgwSZ9a;B!DIqbub2K52K*-c-2d2;5P0FgUh^V+ z%CRSZzUDZqRrL3TwJ3tFe#{v$_r^F6tgdHp3mbMzB?bQ4avm*EWg$EHAExzV#yYyB8%Hg~=12-32>ua%{H2V<$AOI3Fzm7fi=JqVO3kIf9(f+m=Y(G`}$wWZ3u_AtlpcU!sM%A z6&MSQv|o^6Y%#5C@(EchRoz)PAz&DZN`<<5Bo1GSpY(Wp2gp_x`}XzU2SMZ@-4g6= zdyt;~BsEP8?QB?E&_uUXr^YA|aC2+hR2-}eha zBIuNKTfdp$`zev+#LF6N0-shK=KB|-0QtYbD+0)HhtAOX`5TjS7MGl;dYFD{T9NAI z;X14kx*8K$B2Q4F&of5V%B3wog**`N3b&!j^U*4BMnEWrSFvkY+HI{XI7GG*GEPYV z$lrLzuL1ToybG}GtMW^qu4g9a8rn{qhVcE;kY`$#zi!3|C;DGjlWRd3i?7?9bAR|> zK_CDK?aml>S%@%wRyAozDF<~CXq%)46W`9%qnMq59Cvth{nyi?mKusK{SHI(b4~Nf zXqlUyA9`kD5Sob>W{)~3ed5*2hbZmc?`C|AI3j@fI(fY9j!@L{R(9LA-Z!=fMbf2nJT@Zu66l2T&aa(2&(HK$&=toUrf{-4T5 zmeajq%2>}BsF|^HUu$6qkvTQ(D$c}xAGu$5alA>5+^^HSLl2(Zt@{!`xuO$(CMJ6K zVicF^4>IxSiUlUFxhF)noofO*3Rc5;3Gk(Ys?4dIcgl2C#ENy)_EpKHB* zM$zMSQmDuG=qSB}2Ml9kb*Fz-Ud}LQlAA&Alkm}#p^qLjN*qJY7N6X{OQ~Z|>Qv~} z8?+dg3N@}Ti|&hXHQxnb@8cTW-mYBb|6?t`BI1+F_gG(|82 zb$nkk?g{E48}s2YaXTw^9g2QJMGb9#fBWk{3G>`qw zb$$ui_YaK#hR_XbqaItpSHDAELe)N(ml^T+;x%lh_dKpe>X7MJN6F*5v)2csfq;qX({hI_W7uK|37 z;mn$F@=rc~dsRaWe7nr1ykQBz4*z3de?f)*Ghlx~1tR-5?)DehUr?dHgUJ71u)m-} zKSlH3z}x=;>@TR$|Gr55BVd0){gJYNSAzd1N&ct6{?aNk{Qm&hUr?dH=Q!7#(4MP3+l!>JvK+L=3RvNr7j$y z5{hZ2&xr}P4d41aUCR7MS@l;V@mox+z6Z>C8di#wkjVKms^pm@9ZSrc`H(+rQObAU zhEQcsCd=P2Zk6Ld8XPKA@69-M0DjJ*@U^~zBaBz4_9^XlG(gib6a?5-kn9!CTdbgD z;JPbh%_}u6&D~JpPu7E{bT7A?M2y^ixA_ivj&DEewwZzZOXXtrQq^LgUJY(VV%>g= z8ouA-@OL2arX9XKE-eJ;0e?nd2U)7aqYql!*F9GHHdj2U%pWS&M;A}+8;?C^gN;!a z)>Clo*30xS2q|q5k%YFn1*p5fPFbW&up8Z(iHsx(v-;BRDI6P z0tZ7+JNu(Sa;l$K*;w}~IBs?bD4_-EO0>7Xz;F!81nZn8AZB2Iv{lRrpzK7hS%(7lcdgix>5@=f2oA-!EZn#*$^u0Ct znVw(cpp7%xnZ+-#NEJ$fbi_ZKMfi@#-zaV;f?NFC zFi1S9NXIaH`h3xl+|6m&B`3bCy>%Kdd_Hk6D9(Nk6}QrTUdN*G8#ck9v?O%Xk)>A# z(6`3Bypj#%nfWxoQEN5yZJ4W$moJ|Xcj%)w!uU@9TN*D9#Gu42$`*5D6#ei_T3VMG zz$@EYb`0-R0SWdeUcE3+E;K&$2}}_*H2gdv6uw;&y&2-&7cOw;sbS+EnJUoF3S!uW zMlP9Jdi6GQ%Mv4&+2Jp?KTa6}l)$jNJv%27P{N&H@q|TT0yefZ*-vC4aN@aKIbY3_JO6#opdHe z+m*h0SfO&`w`0I5e4Owiy3g{I*Kp?1(IigubCV*QCBZ#2_QKHADikVf`@#5k0#iN* zGU3qP2CoBlhXnzf7d;P$S8pY2Zv!8wPuq%L7AVOPGxgFp9F>MqP9f8B79zjm6d-jLgrvN$==IAy+1pqo*Vx;$z7-Zp?+8cOjGGOzr!(P1C zxtm>wgxfcSgnOdZ_i=a_Utj)Ec&A3c1vNKHH8VcE9*q4!yDTU~WF-Ojzb-SPf~n1pppc7u-1mzP~?&{TKgO7ZcW?S%_~ z^)6u^L#fxp$+6~O^}!M-Lz0c(@Y$J>8Z+VyKFDTqR!Hwt276DjklvxFcSRZBOMCG4 ztaIaE91NY!80f_Op>ZN^@|u0@Pn|pVN*Vhi8rjBv0uu&rVhk2J9-1c9!;wu#omAEN zP|;PDwJU*KUfYJZTLsusrsqdIBngajGfk&dC}576ACnl|>b8?~wtC5_@Gqc?{Y05h zXp+KkKueHI6T+{`n)-_Q42qJy_PlO__G~>`&mG3Jx<*) zqQ6!p&iT`iH3B0Jcx_chdvF`(W!Q9h%)x_gEBk|c91a(DKGY_hE$Mpxn~qY_Fq7TI zL4QNl7N8oUOwV6d0-({DdeA3OvU=P+t+ih!Y)2pkjUkloQ^yF(x5tzPHGV|jG;a6F z#zWpsw?QDAk;Aj$o}}3|J|Wxi!zV~#%>*Sm(R4vAmERbzI=qwDwaD!)8P5vhf^XqA2t2EX@%9Ur076ou^y)9F3%ES=|`eCxi0mp<)YKU&j35rx^1OfeVO23nD2NM z4r~kaMPTeW_varD@PF5=zyAm8U-DAkNb>Fc)B_JF;$=lYR5j)efMnor7GLS9Cx0Qd zE0&eV{3a&Nf{X@@ZSZtJz4g3mh}bX8~=jwQeS3So@h&MY^HCZXZ#r$2*MC{ zI~P<-7NDIrM^{!07&l6DHOMyTJV{8g|GqJu&#yq+ER!W)gym<9B&&IOB2LFkRV3F> z?C9E5Zj8F&(Hn*~Uyy3t?hi;_u&(E}zx82wo^X&=uc+?qfG`ha*lk6sQvlhjX0 z_2#;nj3nLBO}!vgIp*zS>*tbAi<{b}`O-hN=9_6zwxtPsbfxEX!E({OFg0oc3S%j) z(MV0}-Pfi|1JF8KB%!3be#V8V8kk^;HU^4DmScwSsRuI~=zSs0a>AiobR=vj`ee+H z-K(-4^qdScoqsTS^?fT%UL@ozH4h;p3w_=*V zo5;Wuv=_@8lplkdDtNEXc#K_LxPPVGG*b14uzpuyWp7F$Mq4M#1V0Ns zVOAj_2iu+=b+B8-dr-N5BUyl}j{FIZhGob!>5duS=>5@>3)g?FkU+J&Ex)NjNs(jB z8LGLp*Oh;7xPZrD?!sR7juL6F)&hOrgh)7#cPpva|L8X#5}dUaA4I z#pDd;H<|1nFdRcDL&%3APPtpC79okLwb7FEKfvt>RHdj-PUL3F_dRJ`XyOj(e*&z;kWV^;C7c^DDdIPae%X)t*9;#=YTyd(t$T<0!L|?fyWsq z9leU)zQ5~qxqg^jQ-~PZb#-cm?gXFDjnJ6U51dWNx+68??aaN`ezV_CV&3jU8}gG( zjHAb|bET$Lxz))Ih=2`F>}H-{+6pdw907(|f51CpC?$HdiKSFk=%=U7HN6~zPL6!s zr^FWHO=q6=yyhJ|zG7@W9+J6nTXW^$#ox^-{?g}9w}hgRxE0CWem<#bI|XoW{Xkef zUMS94DFiraCScf2`TPiFi15db)HDZaAsOVe-T)ut!Qt$%?-GA3;cbJG0!PlUR5i*7 z<=wI9<%e{U@a>!ssWQ}4Lni0RW;t>x-<)O3)riBHV zA(da^$;m0CajQH_7N$EQ#f&pDxRgmJzlO43`pO#$=~ge?V6tfy;+3lwnFU=uxv^%3-i&$Jz$6zDI?WL=gSCNyK}T2`C&;8tsuS()8xkZY_^y6iCrj`nUb#6t>W z!Dh|@EA?ob30K!R!XqPryu-fL{w@93mvL&>b!$lM61Kfn>yz_3-42(0qxVSo%stwA z>FA}Mn)V=zJQy8lu_r9ROZ(#HSkb|y?y7-`OT_JY8eZfx4GF~JRtNKy_Jh87r+(I|pW^Kbsp42+W#VZt}@xo);+@_9w zOOv!)P_lNugOJe}7g=*O8Q(XWUhd=jUd=q8kz3w2<)DO*opWi){8%k%$#SV$5|O7s zn#!*^d2(p2@s_!!&ud*h^9==Zh9F88MkKVxLj~>cO^@HvZE;Q5CY5AkR%8z7ImEf> zM?oa_Vx5b|Uzh^5uA%#>W}vs(kCsFDo@!T^d4nHPd9XiXV}-RlfQZLcpRgh%n@F9C zM!#W>Uu$SRHJ-;NUM$x!B~foP)W+Wgm#7~t&%A-GC-Hd+bvmgHFk|bB9)pZ@A3Y5^btyC$Sw{jMCDh7IOSThi;#0E=X!!lp_r(m2IpX$ zpNumeGzCU>cXh2towUnX46b(}`SnD+Ddu`kW4Ot%2?0AZ8y0FH?(G^RDO!_fme)k* zI)Qx6sPde)q}BJznOE``X-{FXW@WG0=_W+OQtJ~r2z7HC5_Y#?C2KWSE0DD*gZ(_O z+P+&GG;hQYFSV=_!b#cUM|xH!BAvM9s$IIcTv161;sKt3@?GL14EB9lh}k3o3_BWA z>ypp+N)c9GNW%jw*GD}aSw`3!*fuTYZ&%o4G!$A*oy<|eDR@l^h z*gC7LEj@&0wB94XLflt>CQ|tmkk<{m}jR7x#Ga&*S@!eYkGyk&G z^+pW?_t1)*g!4jL(qk)PZ<|e!s7WMUL~Vs4{yyd~!>m(@S$?4c;cF{d{YKZ66qyaU z0}!mYFT9H^K4~UxRgQa5Fq?SN-07nL)kUZBi4>P17!*l>ocz;jR@&o@)SLM8EhN`Y zU0WGgkZ-B%Tq-xrE}HIhsAMnDAB$_?n!Q6BjVWf!J#Myhu45n6 zS-B@;?>T0fOyj4h2z^^P*yM~Ly# zD2G_3z(O^43BO;*z6MxDU0-Ss#oJv674J;jd;yiVRiBFAt57u2B);8H0J)j2ZehS= zmk%_;mq>?mBfBJ_pDqb#tFm)GVPj+row->PFzGVlr;@wz)M%79f6L>QrDta`I?|HlM_HY9XgLT)lnC(cNWv1Vmu9e|k0fz3?wj=O zRboxKC*G{(7Rw~*(#g2jB1nx&qrREe=4dT@NVRXl+56HhlUK|qX9E0ZUWSeqK-xLP z-WkWp@Ubo2K9{vGwU5*>zBdxOIPYvm6d%7-5kMNN9QC!esH~d|Sn|Y#=?T;455+BD zDzph3=3}`y3wFV>SVR%wqsi84G9HFm zy@v&hcPI~xUgBehc7kMxoyRWn$niyBxpz5RO$mTFq{Hf4w0A@oq)2JIeSFux#5E1c zC>d8uQi&;geo8I=+_r(-lPi)_~_JZbEznLmur4%gcB@Gy6Py+cb%si0ts{`2p%#9M|d>Qb_f1$#r6%V5?i*)=1#mK({7leesurn*uxczvT%RQklEmdQ|` z4Ry8@|Gb8@ef84Pph4j%g5>*3{kS8lzT}Y%XSHQymX5+#BtCpwT^ven!SOZZMUbKhK#slW?HBs(D-Fht|dQuk`yvjI4_E{R2|d z)P{L*(K~lCL&q7&%uJm-L^%Ks+jO(8jVgjMxyQr5LQLR)I?-->dzbQwRtitL6wbZ5 zwqtzPLpNnS!wyVoRYrjkETKH!?@y41D)&jS@o-k(*M8iG*0 z3tDHQqs0}=klN=*PGfOP$P{Ar+7iGe!Q?P2ubTLCx%uO(J&{(Ipf#kJm!&9kH&Fxm zmM_!|mfqi9HF^s>1ZgXL6)48Sgu^w*Dy<`jWkj{TTN^<7 zU1)JflXAWx`$U#oh0H*Jr*|f&2H|9`&#I3c#mx|YEANmI@i_cV^g!wAE zcO+g7$0GYMr{`a5j=rl^xI}Otpc3R{_MAd`h9nJ_UAWZ_9J@%(W@-yBRY?WHGr7v~ zW-X}+Yn{nsOnc4UQXRq;Ye!Ki59RZzC}w#w^>J}fb78F|oJQeZ9y7W9ZfI#om4J=n zL`Njd-BR$Fdv|$$R;|kP!5Zh3<;wLgaSUQ4dPZ@x(6<{BHSC^CynZ;Ek%AezM4m(v zbx@=z2hnS@mT*V`1JF_Yv28|E(^;G}QrnufE)F*Dot>9F|h1AI2qD zg&aiA`(C-2STS`9vCn!d?bO62jOK?IAg*kG8I26CtI)``Whww=1 zJ`d}efVPCw;t`(+fF`FoXV*4zhON-S!j>Q|_D&qsjP77$h9Ho3U^5Zrdy0%e2%5Lq z=vxLt9EEKm&J3(HMRqF9Yc3@;f}5UozKC4V;0` zHo2WMlFiPZHS3MKG;OmoA@<0P*%&80FXguCG=jh+M=N?Pa1R@eT!7CGgZQ=KC3FT% z27gYrW{=yeIuBf)O{9v9b@ogVhXHNOE62%QC&TNt2g}!LNiJRK#Yx`adW@T&a@IZ_ zx58VCM_{c^N*E^TZW@BEneGapL*L;NU4AoO91Z52cx$ zgpJPlB3^@mgsfSO%2l@!7_hWB?y{8+r-<`$-57Jnu{}$VG2Esk?9?*1$n3;mxAdoaPbJRG8h01CXz*iwxZS0`a&=2C zPYawbQ3XTV99~+sWaAVB8SFoy97XqrGtv>-5PEd+y&}fX`|$0rB?@XH-twlj4_@6p z59k>X?eT89V%TReVjuUC^w_7-{0s?-Zh`ncY4y(hTm)Tohl4!33;*J2OJSEpW%Q`GT{0Joouyw8XvmfnAq zn!3@9Bd?-6PckKuNgY#DCLnlj4K`An6EFg3in8=jHzkLak=_fH*roNsO2~>-T_kBZ za3p)yf#0y`E2D#@>yRIoC^25vsBr#BDl@BDZ}Hdt8q|;*p?~a&>JgBoC=w;^NPaUk z#ACo?M3!anf3yP5e+|#J0ETk4ih%#vBc(#aYyx^FRMnOzE=PIPACmIYjbyYJNOjw5 z*4G342-bIGv%z_kBvzRjfcoJ%lHa9|i-H(41Nk_gbcR?j_~PkVp>V5_Q`0go_2Z!_ zL+{J@B5gI1asI^njt=TjR^F6`92=+0gq0?hojzQUO20x*^t8@EsvuPf$OK@XdS=6t z2K_?8hzdlsxa_Ik{+E+cR^i=fcT@qv>L6-s5?-fYBdtLh)Bd55c$C1p`*< z_==IrYw`A^59o%iht8Ijs&KftbF^<<6lxbUG^GOwwAC#w6IennG{n?;(Sx)-E%R5Y z)sC#^d(DAJ-7+k;s(0{q%z3%Zz3km9nyV{aH;^EN;r1j>e<{P| zM2FUkOc|*CPGh>`{I?DEA zo?Ho<#kHrD@qsodZR5A8kUe=C|gK1v$J2w^~Od*UpI+YqfHRygSM~kc6Nhf z*b1fctbLYv90)fBW^gsvaxS>E_OWqR_oA_h+{29aGFb>mtb0>aX25C&3s+!l-MJL` zMRf`6mq~|r`SWWdr`7@pu@W~ME<8CkNvsX49a_xC`?b(b%Wp0G3GdqFDB-c*5hY!cDeg%exNph%Rb=#;JP-rwl4C%= z`O}oUEr8Cx{SZo2CPxh5pvd>iAGmqx#jw@w%^sI7BV3OX=o7b~zX;4%MBfwYAY%OAs|p9S$>1hm+4H0_+%tMowuX@FxvWe_x05!jy!oeF+!#7a-Mi)^F1%^G- z_6oVzW|b7`7PEs0+0@}CEo+i*_vnsZv{HfQ-1>t?WF?FTC8^UoRp4vT_lJFH zy5tCP1tij{@6ueD)R75sG9Fdv7YQq){r22|(0o@Exv-HG4=Qm37=ijiguF;ouI!%d zwOO^4Qs(%P=s+q~s0lFRP|))TajrJUvD9>EaE`Nzyy++f>e-$=D|z-A*U z2a*rA*-u>8F18jg=hdN=;Up>%dOZKokb@2<`7Jgs&2=Xf6S`z|YFG2VRWpa_=tj%@ z#ly%cl)nhoL&c?_U5=?RgfOS=S}$&|F5&zWC#*;{w~hu!cE)%h%1I>xM>(xa z#>HkEsB2!M^mk96DkvOwwI||6JlCuWdYoo^D74u8zu>)=qo#|KVzu$Zfd$YSKT|Aw zN^(-~69ilBwH}bR&^xT_4EBsvii+XuQhWw$r*nG4{70&dURg#yRXuS&HP_mo7`u+% z7r9!7gRfXIiK7GbOl=34BuDS}5nt8_8xB-Y-N$f!9HS|+awU_l0)+O@VTvdOufm<8GY2)-0`OT7RuhGC%9DgmO$F0gK}34{U&Uk9~afQi5`;j z21TF9JP&gjV|JON6K$5@)|kbcn-l7u-Mh2|<5xdfa~|MTqYs@%wMIl+k%G>v!(fmw zRqUF)TWEaI*1B2009BF`p*~2#p zM>@q+HLa#kPkVzLC^A$N3mjWqlY=-Hlg`%c#(KTZLN;|#Colr}2ybu!AB=ZgOn#mo z3k&Cxl(+JKKT)h}O6zwLhRYB?k}|D>iZB@Vad|}=afdA$&Dg#vpC5SWp#I6-5?~z8 zBrbp4*xL6d5r2cgEb4n|_paCWsBn(vX{Zbr%(a zV4gU=tVs~VR)OLSf6eNP7yj|J}0Y*y6H&vNl+4M!6az*uD&2*nB9a^6yjB*HAGELJI4I`NE zY@lJ(rn*}WB$H^4FJsY+poFmZ86cDL1>gOFJK2C+zP)@^iQE<2+HSEr&~8;A zH_3IhO?1f#k3d;@*b(6cEY-Bu8}C_MW-;8kBg=uVYmY@etq#Awz`Z_;;d<0kvk~XD zh!F6dOOEys&_HTcFYU??S;`R~NVWSQJii$ov8YkW4ucpBaKfA%CCJo7qzB;^b4>G_ zTKNp!j+l{hX?^7^Ld?C3=!MEFb1R6vt99(c;@Cmvo8>c=rA^$>7LH5qsQXZS#2)mW zICq;`TumCXz`>Fm`rMvHjuQ@e4lSN1o-bdZzho0+bTw|5Ot|I#fn0T(oP205*=wqN z0HJ9QA(OhntveDqVPSb5*7>ab8gld?F!k3^4}{rtRd_tnzzLw;tt!msy!%;~nyXnk z8Nn@3faq=B_2ak(dPPsmNUO}GI7N?lTLryMO!&h>8m`)qkMTTF(-q~1n3Q`~cP=Y~ zT$3H>@Yb{f`J+GYkj}3jM)Uw99Rz{-A#7Z(_nsnhZ)jfhST6rd#)Xc_FJ?pwZj5A69E7&iI-6a>*3)?#xe;la22?R2;9o>aZ!a z#w_=rNsLrD%sAH68b!H4Esj=z>ush0< z_my1$fDp9#w_{^${DEgdy1?=#FXZSG^3WtTdHzcJ9k?RtXIy4TZo@`j0jo0W1I9nO zWyBo#2N)qy_&!1cT6e#k(CC`+^|&KtyAJX_0b2>anUh8X#;%TH&U_zh*`}>5rKLi= zQ3Y&#!Q^vh*UVW|d4PC%)1M#{_@2T4PtR|~?th-1&_6`aU!oltOR1)GN7O*gU5gVI!AT>?D=u*@;S;A~b2zg-OkKgVu zI`DJs2#hKgD&vogWy|PbR!;4ram!YAzs<)LFytVy(#R`YOgRh2v%z&vyxd6~j@Gi$ zyW0vy_3G{TDqOyN)R14Tcr4)?{)I2w49ZF(&ry6BD)gWb27s6E{nuLh=lGugfrG9i zxuz>`GVxMPZImHAi*@}+Tt?7+$Ic2mkcUxdsd`DnHWHf&-p^=6OfD}7sf=x@2rwS~ z$Ov?dvx7hMkB?!%wn#cOiI^yZ6DW`4RQ{qqf3jO~Pwq;zz}4<_SHd@7M;F%-V>Sa5 z!fdd^?qF=;vS6SKlz1o_5%7F(pcNGt+NQ_LqpPEOcyc$zJbCiPlUhDSxt8n+IBuO5 zURBW@{h=!rcxX24(L-yQ0=w&3GG2@Tv7zpJWKPwTOg`%@TC*EtNh)|-MlbKo)cO6 zZEt_s+CqDR#I#JrIs1dH;la%71nS<986JoFAm<7{Why(`Q)aARzT(DQ}*P;|23h{0t#BUxA~6o+|rnh9#d@Zq|LamZR4XX z@vk@CV^zRVuq!{bKXg*&FpG|J`5jTauir1sPz8c3bjP!&7Y_E3uD<|=H<wyoH&AsAnjYwIL>Nv8EFNd)ntF;qNuX2(jQc);WN47}t zjzDi=`p3vM3+Wq((}&3yCD1?&$H=4iSIl!>{~W;sgSRihSIFYrJUXB-!nF@&$VE+_ zXJ{HXa9Cb%5-ZXN#^@Z<6t+p*>FN2`lM~1aB|nkfwV&aIFNzvY(5y0;9?v~MUv*u- z>bM8Zp>H_hO0q39 zH)EcyzC^P{4$+P zt!&F%*15WRX!FZE_t8Kgc)0tOl_H5eE|=inGM^2k#4dbWjJV%N&a1tyf66iP>t()J zAbidgp0&8fg3Aprhn-ejzHREG3)!^wouFHw20IB*mgd{}v(qGd70ecLc4TylgZukd z^LN}GC5JDirMI#!0;}f%E{*yXjT~O}fI;mM#Q_`F_8VW_LrJsBzqlBX(-?+TTk}Ya z{5G)~7y+i^XUbwP-Buz`Ozz4S+{5rb9B{?Stb>t`QE2AT$(Vqsv#KzNY6rR648prx zn%4oNUT>S+8Gl(iM#B45O%2#Xr%fM&OD8@iz8#YbkF*!JE-yv&*$dT+Wy?0bfTFhUQ9* z=)RlXwL=-dO%4J0Oio^6#)K8uKjBB`vvcU&k}LUF;1|`4J*Fm=tbcvrn(yx!c2K%V zGPGc33+UsF_@}4sK&sA-ZP)&f=si$?m~rJ9ICP9U?=>YwHLZO4e81O>(OK2%v*m$g z&6fq|8{lJRH{KC}3FxMWXZ)eeBLmo6Zm+5?0?JRk1BQ52S{C;Sa=x74&+!@TcHeQY z4sOs|Irs24Xy&PDho#;fppp8iN5FHv93#sj0=mvRNDo#e-gVFb)`k~Yn?z%`hO^** zX<9JrSGsz(HQ?V#pGtRH73+5F2x23;0B*X0VXU7K{4wfk7NO=dOd^5GLEzDSXUo5h zqyJ<0L;tTE_($+>dk|t3FRAokKlkAnjghgP_H$2CgKR#2u8Ew7^plmG&zzIK`u_Dh z(Rbn&rr}g8ti2BQYuRGCO@p6St~N|vY159vflYg3bRXvBZzyeW8r#K!vB+lqE(=Oo z5Um}f7G?fd2>^ZL;72>S9jJ|s&0t1YJ-4R6#N$7%o=;6~eyNfB2Cq@j(HniWG{~A2 z%uNm4VI)sJ21tx&thYFC9h!#d*$p4F<;jVWOQBcef~?pfJh(v<)UNfoNeCSbtK-e% zHAuhd1QSaya|=>v6mgUSM z7Vw4nIXO9wD!L4m=PJ%)s@bB3RrdT*^yDjvYJG=l`MYz2o6*x3*z~1Sv`~M2H$8!w>{fBYH1m5Iu=bkSG!LiXfs!A1!(t3{gi5 zse12S^iHBi|4zC0-tWEdXYc3P&-eb`zsU7VGG|%qSjRfnTG!M%nyM9>nwu+DD*o(V zjDPKLB(*yG>r>ChqVZg;iQB|5vdUoMuAH*m&Q03Q1xPQHL79TvD2?pzRcBbxl;Sse z9|5h92L%hdf(@~k<&d*S*;xqNnxVm>k``c?MHr`&t zG=}IR>iL;qx-Z|NO!2QtF@859ke3l?L?FX2Mo18o6Nlj;HD0DHoW&=Wz#|A{d5M~- zfTAd#5)(7R{P5tKl6VB=OfONU38=fw#C$% zZ4fHJ;#ZFjO>wx3lJHYKqiQHEQ8Xfc`31~XF)BEm#<+mLqwcm_POr7WClBm^*ZNOP zvFp&W!F*0%#w!CCNMx^L-8_EY|DIzsv{i|XRy*;#jB9MvnN!;T7A!i@eUxSHFwanZ zfO>?aSc>y92Hzx6TN#JKqS;NxADGftFLtT&{unR+zu5Lab`AYE+g1-ll|G)kw70OQ zD;{LinH7l2L$fz2SeW)ic#6MLtrO92L8v|dxp-3lWum1YcCOWgr%0naSKs!QA(yHE zVvJ4UKusxH*lhO4nrTT#kcq#-_Wn?GVC{)zrp97pwUytE5EE#k+@#oK>{0qgSE?af z8yD{0go@jE_h%1mbilahlU$8S`2-_lzbs$UOw;MK55;!Jb|vebY{E(>6t=Ac7Mfg* z(_-TM#<*ooH7{KkX;u0*s?4y!#61y-UKuq!Oe29~2J`vOs|5JFloIgb`*z`hPr?W# zUrM|r3=1S6lo5nZO@aI=9exILLuT+xDG59bJE3|UB?0;Si{RNv#%Bs|Mwk$@$|Hsw z$Z$S;@578VM-27M-m+{{T+e*SHyLT^MxVl+@JmP6VuQxY7ISj$I2|j;56yF%*zx$K z7}CCN=5PrSJjheuYK82>+z=eJ4W$HKb;*h5jSAzdP~r4z+K4dFA-e)DI{w)L&k0;Q z^0-9u=7~#Y2c64dZ8g(HE6wnQ4ypRa$uZsT@cPs#WM(|cTr9Uy636+QPk4-$pIa8H zRBKL6nS>0?w`VljSD$pO^SF`)jTXV1O_X_qK!&7}x7{mTJLCIqc&Hv*n(Eq|$ z{$Vu#jm?B5qqcftLwNU-&$s$vl(#zX?aw_}_(~SjLdH=_d;y)XqI<2 za>WrEJ1ZDnZL~1S*AV`pX(RiS@d4dtOrQWa_y&ig{d1c&fg;fLd!rWa#jU~0dtWZ5 z&kla-F~4Vzu{>Dv*?`EDc{>b+-iME#;Ks!Vl!u|_*e^Y6o*1-KWo^;VZew`2i4XnS zLwT7;one8W8JlD(rGY(^h6i#=m=F_=D&awx# zCPIhiCAfr-pOHo`VWtS434mu$B1z>4U!i8+-0|}alb30)A=-!x9EaSh^5aTc3}Q)T z#+~ZLWbN`iml;JtCFZA%k-6#2UgU?1q*B}I}jjmF) zzP4;obin4p`O4KwJE1vsd_@^{HE<4DUR8Rv?)Gu6Yy0O$lqN*pKFVaq4sX@xR|g|{Kc_Ik{O(5tIRwbdP+777G^{5lu0Z1f5OfGanflxy zCmiK(@0@OdcYVS znCN(^%BmJL9-vy57+?&8pPUEaJG%Z&b(oxMqRMDru)7Tu|wq zd9*|8&-LuArs2x!w&`(X7tWsh4}2WkFr{8}v4EE899yrI8P(SmHHxkwV4mAc9&rb& zSt${{6^8sKc>=b_NDV)0vzfjo+&TVa9}yOq&|TN(7p`L(CAGu;agG1KZxQpEuxo6m^&B;Lj-~sM&UfD~B4xdGLrv@J(KX^S3 z6*@lfXE58_O8&ja9EdYa$fSgWeC*gmaM4vM2yBrSryyI)JMnvq8`(p}PD?u}5vURk zh-hzfKq`a>>=JtuHshzEJO97Cz(4Nq3K~_qi^`=_6UoQF?6V4d71l@X?%o?-)|GB zMhAL{dnu|WRfn;y#zbFc)YM&3VYg+PQ#jKBgu4`u;g$D*+5u`4W2f)l+@t8g) z83k>9ZZmF~9BVT7btSoPp_+_lbMrFa;n0A~9`m1wPnSWNY4J^^(#zJJ*-=cs+SdX==USDHRO=em+l+3nSZ!gfX*l(^PjOsvp8F=KEE@_V}OmoAm z%2=^gS8iU!uq#s3)hT;awRKGS3NkR>Zs5FYfxfcCt~y)w-Tn z$em|wg)hzdjBCz2kxd`ejBj=9RVFBa`jFL6Y7VWN zA9w8{)ZEjJe2GF`y%OXV3c?l6OH3Ewsr}jQg^>aIgRFNrMPKe^cZPGBLR1-FzcFl zXV8TO2g_+n1p@KEK4$2j&iQUe+B-JQnF5lb%qg+XT=XqgJwf00P3minMNzWfGR8B8-3Alwyj%^H%ZmJXNZ{ifNK>k(2rcCG8?|> z_1?hi*NbS^f=%a;+1k3DjX0Bzm>YW|*QY%~U6o`K(5q;i7ypo`agiI%-oe+A8_c}W z0Rx7bJELWDz>_sQ)Iw)`8w5YjQI7TF=ZZT=!-d|WTW@QH`W<)>hG`}a+TJp7Nx(?$ zE5y?0I+Un3Uj0WzSDg;ob-+>HxNuSAqBc)>c*Inlgis_xiWRdL9ewE|wV%6_w22VBw>EpN$N+v)cK!wQ{cY zdah0wZr`OjJODk14cD!sYkhVYL|d)!;VZ9r1A65iyrbRV5&g1uUa@F*YJjL9NA-s9 z?#nZ`EN=902tcq*NB1||1_RpN<<_pWuqL}sYsQE7&8 z>u@`-0GFQALGjXd$%*ZYL-oJ~x@C7`-xU-5a1!cif zn5OyJ)rmvKYQY2TPav$Kwp`0k&=GD+HIPt8*=||%r@AILc(&lzC6%G~VWsSeZmsdShiP#X;6cq%+_waebA6l^&{|p~7-CFg zT;1eOFi1uujsBDH1U(}R)PpxSwnn9z%r|!N=|T^$559lRdGD!m1lNb!)Mp6So!swv z@0O)xqaziiW%Qaq0Z30*^j1{;Bw(niC!3XNV+_{qNX@PI1QqpF?~UQKs!~T-1W(u0 z_UsUy60tOVI5CP-t^L7G40SQb6|2m1(SZcE=r7EjHoj>L5K@CZhnJ0z@gemo>L2cn z3ELH{+a$7=8kkKZ1{L)nrzpa^o1~c9UaVrv>8DLU%fBZHjSTM?O!CsyYa-P*5;A$LmBO z%^uI9xBX{7R|dTvEjgK=HfONM$dg%@v0&Y1#^3{Rj%XWn^wB+r$jknkna_Iy)3n}- zw&?>BSW>IUW-{J7E{j!utgv`5tqhf`rG05tlnJV8ilZ~G+J43&(@gsh&LSxQ z(}%vS((oh0{c0Hh2L|=G09=MDosx(U6=FFcD=plzV{6%AYx2dqUwFQcXE*g@9G^r$ z|1E1vtV*?M->2!czVoG$5={&2R**(z;%H|@`Bttze}{+VgitkEbJXgA*-_W!EP2gZ zQxYp3Y|RB5Bb|6HPW1dQ&+PU_!G!0{#h!Ar>bcsgjd$oDm**R(o)yF(#%y$&99QNt zB6+=9=)0Op<+#K~d)r}3-)Q>IzZNKDY1^#aQL8SV8h^2skysmMZXm~y*bgbQv+DHa zbK>q4azsy0dlVRlp)z=^jB5Ot$JUf_VjMX{EmONCI!zjd&L?H_ur%29!vyh_qt#MY zhqZ28v`HFsaP#G7T|ziwcJ}&L?JO{u^qWnRkr*WiTExw(haInO? zU$%naIm%3HZB^b|`6g8SVx!z+Ag#E|Wc^Z9RU1d#QCMJDAn74diquE290J+sF^Yd1 z+5Rc8cO}VD|TQxVf5kXx4sh^U-5F1x=(uhKuO+b?|%;r|b9s znv|6mqiC6GDKC?n(wqox+K|}S2ema6pFA7MKcX_z;bg@mD9}_Oe#d71zN^*RwdtBb z9IiZvZJn#d%@SMKFxK5dmpApZEZrh*JqQXoF}|C0r^cmr5fHMLF;%)rNx%-{;<)7D z8~N64iH5vKoBYL&Unn%X8hb>w{P)L(^n`bIk8lx!6j;1F+fbg_YK>t;qLZv5IwW$q z4X&_0iO6_%u(Wa9(<4(jX~hk> zqnBx0Rtg0h1q-=(8-joJ^qp!jLX_#qt}X8VX7B4mQ+dz3bES1IJz*ah9npqs?~c8? z{J%$g0QN|@use_8S%^;`wba#L2zfd0ZT8s)ugaZ$8;4+PPZA7u$!A?)9^mAADkwUTMIz`F4y|4g3y`EbcLcSycyuI3Zj4~Yt+93) zj;pTli> zZ5*|Ct8A>!jmkW# z#pBR+5zo-$nr^)qYt**8(-P(8;b5v*ruAY({jyjS#-YJwv-qA;Z{AI4aZu9WIy&)vEAe0b)lXco8*tT=3WnbI043eDctFGhoWmR)G={HP+ z`Mk24J}EB7+Sh5R%}>1ys>){?{H4{Ewb2@t}(k~c4{vyiYBD^0qeWlgSQl&U|xG2g=% zECuiIcxp)P7<3t1zWIYJDCFF$q@ABBc^L|R`SwXm+H9wEE2#&oEM8F_^tC~jwxOKi z9_!n~_k&l0n!kO!)_4AKI`c)Eg?5t7*2--t*3KXK#b&tx;u>a*#I5qrb+2~T()PFf zDlCT(BTt?T#Eb}uWfk{*xJ=#X9aSQmJno#=tRyxPUoyv{($Fq8LCnIo*Y()|nwFWZy?jJ(DG~gNAy<%v1vZTJptu}* z*R``9MlVjcJ&2fSr+|f(TG4}#*WVf^mXU&!5^2r(;t{S$GSX!*W9MAP7Id>J3^eQA6SHLQwKY?a3I4C*Pc%i zQ-kB5j9C2MkLmM6jS6|Oe;YjJgao*R{TCd$wqJ?5x5*1ZHG#O4SnJ0mVB%Ad=+Vvq_3d<345GVcL^F?_eWR`Y20}oX7Rz_FC|#EuvFvKV)X# zhCL^zxxE6G!q^!dfkqGGm=isvHitRl|x#M;Csi=)xPrUBCKYLU**3cS(VF& zCxH64B+H`_E-o3qLpz>Uu<)h-)$nX_Ct~#YwtKW|od1YmI3)6+Ttd@Tkrl2++h0>4 z+K>5OK^iDhxG68A-NbVVNV5dqPs0N5kae^gJVMcJl{VqFd2;b{OGTdcDyQ?Nm7f?4 z$dnFGi~;lHW>ckYui53B`p9~wyGkhGii%qGjcYCVV+r)A#to$en+#V7MCtIca} zss2v1qrEGqG-6}Yk*{q)Ybh!c-9W}&aZlHa^Cy?L_j_I4K?xKW*c;;;=%8>yci;6lr|nP*+tZgxrEAlay+EyhV~T5K;~ zG|YUo=Nop!%nb9pfVeD$MM?%`^r9|qfOQRAC;#Y#u^UbeFhKwZx zLIWb^-q|P`cm%HuMcnXz{_f6zKG53_UANS48JCrk`DAM1TG*iYRmc4M32#e|AkORmVFX>T3l$8;NAZ7ZX0pno&LBM5cV<<_WRzac_c@7N8jJbTvxPrU?KNUecsVFxpBYwpDu_eL-ElXcCV3AHDmG~EJu11H}=o=x!y|V1I zgfzr=Bi^Cd2N1ImYO5*Fkn8-rU68(d7Knf@bR=JD)H8*#dfSFf(S!4H-cQNO$&ecF zzb=r0s2w0tAuppJ5i$FzDX6t~v@5r^2%BBou;?Bta6}oaz276SjP}{+uHmom*cU(i zg7st-h9l2<@oS>I`kvdS$8Q`IU`dq)ks97_7Vx|!Ss@_Q<=i#o*B#z-+WYnPUh`7& z^SSB*(!S=#FDEM;&icQ+?tkO6Y-YKeQ)(s_%C<7fW>F2&&#<^jEm}*E*@JC$n z`;0Ud1-99y#TJz=Ic^7+gx4DqBeHu&g%6QKVxq`!`aSb$E?0Y3fqO?(bzTYga9LzS zcGm?v4(DnW<@ZB=NHBC0T~5Dzzzh@OvH5!8cYW2HoE?s7)TivKC|ycFVQ|^PPtx|@ zx`QQjs+*XPTv!2n-UAgBeYqvcdpYPz27<@iu|D}b)`f3Y7RmvNt zbDqyl1WOL8SlDI?9S0gGd-H8uH>~$vcjLGajwl8sW^n>&TSDCT$Dm8mubf1R^y4jb zG&azITaN2*v@gyamxr+OO3rjRYPYCWUy9SzLKq^&>7AmBY?bY>(Cr5~rkVQRtfJwz zWaTaUbOlddhaNT6tk{e2Z|W+gtoSVYqpn;)?1SR4Dht4m@Y-Yp!F{>S^=tG+yDlZ# zjZ>bwBWa7;3H$UI6%J9dpKf)hI^uLmKz45Uj%%tr?czj=oA)g_IGmi~I-`0HNE}IF z*&J*m#U6E>3D8c-%D59s_2xT@rYl{|(tXQpig|WrnLYd$WinF^hk-I){BVkD&vpAg zU@S6RiFNFNzGfn^#oZa6{EKcTnz!_MmX#<9 zu8{2`d6`NQnMyJCo}hhWwH8}BxwGX&Lk!h8fm+)F6^7O^5v$Y=+}^~}gaVM=b!VHZ zS8$$Y?6|rb* z`h5qG_v^Rg3_Ohnx;nfBLz-f=wl*hs_fWB0$@3>vwqXh*JMsR`HYMdmsE*!_WJ7E1 zjX1@7Q2T`AgBYMS+FbJf9xm|rUV`g_@T;@^cGi0ONlT;54VcJzuErI^b7n)wH#8I$ zxAs<6q0t(VlQIkaMJ>T)KHK|!{La+^M=KvjfS`qaoRNH8OPCb)2U$haZ@IxKJ?m@> zYsxKso;58r635-HaY=H}e7}n_0;LVopc|W@nEd?4cWn$iF7WmENXF7caMnbZ@!wNB zlH8VQmVMDaj3|Kd^hi-LNLvfcxTcG@myjU$^fH>9-og-KreV(~Opu zcgSojbuGV5BKMRiT{aDLEqJs?n6CIX+pquyl?vU`O-d|MMsge6neVk9%R@!!x2(H; z8n4PDyN;ev)wjNsr&Cbs@!Z5E*}eomxeZs!-%X32Q25tW$Np;^3biM ztXFO);e4%F=Q6#HR*`dWw0C~m!H*1D8*dZt$getKj_li>?>V(~%q>qiJ%Ixy!NN>e z+$1zvjC=V~a#&?GS<{N4e`~W?kw!T0?2ebv)Fyp?vc>K@)#x&RwZ^DbceA5#PA21o zT`F!O1({(;%^_Fx$B|_6>dhN zi%Hy|v;sPZCcNQ5o8$2Lrk$(B;!Vfc_RYh$93cZqi1y~FH#{A6cU(fC)?wGq7oI9U z*Bmwui8P=0As%QMkY+H!h$I8V$=O9g&f?b)5y$wD5Bw&e^rq*_eRy%${l7LfY^Z&P zbZq%pnLVj8q6576%rJYai|zazO()x{Ve>b<0px!YQA>=eKCBfhoX{JvFSMzq!^m?W zATc46%D3tnBkvZ=EnLe!M+I(OJ08&#?aA~T9lW9c9yw|C&D+HMF3_SR_j~iqeI_Um zd;zCTHXtYYoyT6`@>W-pf&V%si80RY#x{+!-Emw@i2px%rtHMB>j)>b_qK2#7{Ej_C{wO@XrhgaYlE`%B=L()! zX$%aOJ6k-PP<*@{#ajH+vVR|Sh2dU4`lll!GJHp~Xi@DRb_XKiz+sI^gZv6Cat4lm zI-#jHCYx>N`uX8kFb}RQy+&QJ{@e-z{}nd;Y9*Ow^B;mGvxAw|lKaEe!%4L}6W25+ zq{>LcaiaN0UP90G)^_#}+N1g5P2ESj`pJtsYsN2Z6>=MDM~daf4T`(&PE<&~q7(Jh zRyA(S#f#PX_7NI&2tc6{UG5HCk~_^$W1aiyZh{c;`9_yanKHQMjK7BTW)YJ8kmX0y zbFupOopOvc088s)zkjqhoNw=+XSp@@ejHhA8eqUiDhux>IqP|pzMxH+locIV`Z!!5 z5BZ1Ox0(8;J(KLi)}#*Pg?*pNe6(}0I?=}WE$eFp>&$XOp#Yo$G3fZfYkhn5$4(!- zapa8f(09SFMod7@;9!tG^E<%%$uagKyEe`!Z1l2zl1pFHt`4k;SDU55Qf4H#ly-*+ zU~k@j^uS*8<4@>84rPTnV{eCG(#|MIAG*d_=4|XA?T@roLo@F(6WSbY^I`6@GC_-jx*_ z>gD$$iX5Pk`m~Mk_UvfNQc|A+mAn&67~{##-*m~gFZV|Kg{{+YjI+0_?l1o*#NVd5+yRVGtq%gqkq)PhN3e|BO!w=-zCvD+oevwk}zH0b8*GkcRGCSNpD|F`bSNqb1m_ zn$i%<`<2pg_#5(hCiRL_+JI&&x&KiX{Pra3f}k&`{V@goHP4)VM|gs$PFfyTEU-uM zH3+uTFZU|H<}XZGmj0wDGY}lVsHxI;pg*adk%n!Em<@myYbCo_RudYpW0$C3?83@X z8Pr?c_+IXL`x_5O>Kz7r_tNy+ta{Fw?0?5RANoaLashV<*lEnO{`0^J7(Ix6_26!< z$9i!^(=SO%QxH$e(zW67wI+2!bM_yG72gVLwGA9=UCjfvT%UyA7|O2jjh+Ij;yLx5 z9ZMB>6cqioVEwV8)I3*K%h3e)o$~#SJ7YKV?T?lm=03~BRXD%BRkq-IvRYR=RG~Sp zU;7P6$1S#qC4~@QWU+`)-{71IgO_!lda-XXefO8q+S$~mAZgsUJ}MXmlT^Y$cuX{9 zNUq8)YOdz2bT+fYn6cM28EyJI;?H(!MuokSZbAC2_1~@)^&xbI!|WnDGMhes@DEw- z5pAm$HopyOoKor0oMH|U?K-UDs^av$efcw(1}=ZUUHidQQ2X~7HE<~IiEKJ@F zN@Ie-h|CJ`0a{Swv&qZsy989Tagt+q=Et_gkY)yNuV&1nj$JK2MyEY~YPZVmsC`fa zF}Up?V%%+LI#XVEkYhK1#KquQdtt7!en$U5@zsE~)!r-_CZ}S}u7I}S6c_k6^F=P4 zHZ1@6);UGF{q>@nob`|X!*T~XFVcRQAd1%?%Z zJPha)s(KS;TP)Q;0$gb;^B&o?d?eg_nJ*@eEXrw0!jE}%8T!B`A}Cm1$ez=u#yMRg z_SyyVyqiJR37SNj@8;Xk^;X0LkJOB;WPOgTS7=ed!@Q{`_?-uiDl$ePyM}@m9i|8F zofZ-eoT7>{ZJQQyJf1C4S$4ET&Ew|ExVmoJ_o6BcT;hT@nF|&bhk97w7p}YUs>rT& z$0$3`gRNGWIe%KsT<0?Q{;~0Bhpp5(j`JYfO)kmI9<9%NlA*sXfPK&}ie>Jx&*J)t z35^>l)0%R1d1%qqES9u0-R`roQB#(t{~Fedl6S+}pDX#S32wI?3xh=06G}V%yI|;C zVGMcZs>6d{4TXFCHSU7z&8bA#U55@9m?78wdLSP>zxkaIBfn<_daWLy)H!kpN=ZK# za<>b;)uu;L2!-vY$j)A#fdprcce6&Pq~LINn?!$!$y2$&AgT2%J}poQi#2#h$w@(4 z&&m9^sv`9F#;hoJ%B_SUL66OJUH%-$HpTigG-?M=Ry#JDmR1lat>)?rEe@XW2|RjXpKOh<*YxmkPK?)XlS|`KeKjW76J)gx7J2)wUgcfQ_mT@o zn0j338a3WY;4b#SB?j)~JVHq|VpT?pjAd;HZ^7AJ1z}nHeT7^u<#vpHHIOf(T5<;8 z{7eu3Jz7}wXz0&1EYL&MjLL4j4btFc-QbQU<6OzZJh zSlOmui^wM8fGt*>=C571lst1~FW73!otB{09%$>nP~&ykW!mde%WISRB;JZxNP;hj zA~o*&-aSp`<6de)8O&z+>-PKNmB$MKbWpXr#%?XgH3f8h^oEMRwpf+tp=h!={3;}v zuTwBxd#YmTm9@=}Vi8MCpoDVq?kbS<)6-Z8N&Nt0Pkm@gNjw-182j~-sqe9XvELy3 zt9&yWnW8UXiLq8YFxl;|KLcG?+dP?8Hv+Gv$V}@l;t`(+99VZ@xaY1~`s%Gq&$;`_ zqn%n?39Cs|!KkPS>^3^DFbohP*o$31MR@o5y5fiLl-p(#wvR^wuUs{abp{QLCB7vF zr!4%DH1ItQjYp_Qa|<*_2574%LfR$D9!b1mkn+CnQ#FS`jW{+)m=~EF6WXFN#9rr| zbHj;!Hj!yOrxhh6-kyIcYT|Clw)&EgP`sOJcQ@Y+0bwK@9T=Hg`9{97EuH!JSQ8WB zA+Ew@ZYtjtYl(?dS8SdGa+?-?esSk70N6?Zu!k;aePe~i2Ic~Agjp2;>Tu;8KAVCJ zy|`YqIt~W)oy5Eefv3=33-k*{~de+^nDx9_SpkJl?Pj0RsH)?_}am0jb85k+k9o$e;S4u80pg%qu#(*ND`2 z@Zt10V3%!Ck)0=0S^yT{-3I3f$k{Ius73__`aqWlTA5#>zP|fT2sW(RNXaAlkrHfJ zY4B3=!1%eKt@_xMq3%^!%71(CaU-AJ5R9;W{Y`lgETofDO$Ot{q||=>1zeD*$6;v zJ7pkvFsH-bSTPvM$vUjH;YfaVjO<#)F17TP<^9^~ zYQ8_K6chHff^yF;oFW*N@v1vR{~>>)g##iK9AFv(-fJ+XD9Z-GxPV9ANNU2=Wbl z(AX;1O7y$4HzgLtPuS&Ox9e8UKoi^%fCxFNcOu@LxJcS$MT>$dB}PF^4ygHV?7UP(;tUp<8~jkHnZ`;+uwdWQ1(TPb=ki0% zyt?uF6D3$DV-qKj0Z@hfZ04j&?SM%={8LH+{ceBWPB8$=V0`Y=yQBfQ$Y(zeG)d3_o&^l>Qw> z)bD~tkovQf5Xyc3Fq>f3(`71@oWy1J*kmRUK07;S?tCLvTKVZ^7}7oid;l)ffhQi_QUnbFA)H;$$%%1*KFbd6`e-ZA(F@E24a|ry~w8 zU-AJ15@J!4rqKf{PMvL80ciCTI^`!u$EwxB$nYLss-&NwT4FJ->-+$$oiykU^%yfS zmiMxM^->WMn@iWx4Os|&vQn&`(fEhCW`dHfPt91~=Deydpq)nQ#}Dw=4{5-=D?k@9 z1r+c0p!l0Oj8QPtydaX7(Qr6MR0V!UGKw5F@No5cjQ^7vR$1pK$5_b}Hi$bRO?_ zSNCYb$YWg^030iSh3VfFdFa2C&79G{8#99b)tr&W-~<8Q?~u4VSN=d7Kp*~p zfxds)QUaLgG*b2>6KMawLXe|Of=@j78a%T_PQH-?`*8c)tfi+#U5H zP7Wz}@k=mH@oyTm|J=p~YUj^xv3w^##Rhy-7Etq_NP%Ypn5WW=;Mn9f2hRnduI%9O z@W5mZrtw21A|@J;wvVBI@ykEiJoJ}&y#IGbFFzW_tOW_LRaB5%CGZ0|YR+vdUTO&3 zF9$&!bKCfcNnRLn{WzeYdiuU>Pj^%)yGQ`U;}aU3?xhTaNb>s`UQgiiAKkOJgFZ5I zxYZ;yDB&fOdMsg1vwG~ExbtADjMn=m!%B2xP~7wLK`)-jLyRoKlBm+m`ybbUH?AZx zP5Hbu1NIiv#dBK_{G?ht|DS!}ucP^lonI_}eBc8@t{c)#q1855`kh)&)w++*370&b z)u%$Uhq$;1iW$5eXH87!ulKD$)K@9)e|!sq8IJHzI|aA){lB@jKV4c%^!d_P_!kK# z6&WiPYUmb>nGDb!RZMu^w;9!Kcb?tC@vvUdZl>f$e{7=4oPD^yffl(q%|z}1_I66h zAVzfqu<^j48l^%ooy1i1FTM0HPAX5(m(37aJc7JVBXVEry93F}huVmYUJlpFp4;@N z7-UQGbzO|Dj7>edvz(+@0Hs*FKwJ?6Ji`Xk)E7MuJmBMh zn4f8;2xo>syV&Kw(wcwe+c6TKJ1jEyx6&!Y zCkI){?-IUZ4%18bU8ydJFpYY1!J6*fDNVc^{?g7x&v8SeD_7@-<05k~B}1RzFq7pw($hkC>jo zmEx{3m!4`lJECEP6^{fQy~H2|d)_lkKUv;~BN72Z3Y;;k;>V>QF5!ANS>4TCCT`|U|*ed#x*UuBy79J(Bsym!C; zP^=Yj*A7C>BpDP_68}1FVoZ3r>nRs$#j}gNmeLd^qvA-09A8n=;918;})1|{8MTr@JuQY zb+lMK4yd!}VoMxYO2$8VYQ%`5Qe@^- zv1x*|)Ky@9c7zea(b4HTF+`WpkLbW+)LTz)MhM@4o*)&J$gc>OKN8{te7y9}TqeXp z-{LdHt+L(35N75p04}AzD`W2tiB<4QoGyk&p7xX;{hkp`1ry>|t`$5I0d0kTy?_2s z-Z>xpZU-dB)!qaw_<-556wh}PxWB%6@VT@w*)yk zF%j6Ay86XwA=hFProBJg&6m*_dBsTHy2S7ZIWBo8#VTo|*3w5&zJy?ux$#YCSWssA z%#+y%LWXrwiG^3E#{q{rJ$`D=yI{;c{7j^5BRO#}ATbXU7hr$Pv$?tep!rIKP8VZoJj*u*)K9jTq1DRuVWahm*AheZl&(=7CyMfU#<<3H(kj zV`g5=CF{`A-aA6?mtG2GIn8z>Qx%9dogaOrK}k40=f#&(l8K9pqu`iLzre=!a`1N^ zjOJ&+hsChqgYPa4m2h`u=VfMQ=Bv=yS^L1e`6Y;%(rleQ;1U578@MHhuanLIXwUCG zuSPk&Sbd}*f}WF-&3xzJ+&rtqVPzAMDB{|lt=9B40-5@(Jip`@_FJ*tjBrT@zr8Vw zbJYL|{QR;U7A<4X$=l#`;^|@~Uh1muXqi84$6yg^#nM^lz2#X}xL0+1>H?fmSS0Rc zdL}bbo#^HlO$uhS#6-f!{^+9X_*c=L4ERJgjJE;H$QF?mO(cI{KfeX0xKsEBqhY;gh}v* z^Z>sW67vxVVgPB|<`RJ=CDv)G-mYMIo3M8;4D^^SUvbqo~k+UoT?!2JL)JgFyi*DlYuieqG~&{ALv>hgI?G z%j&(B{w5pec*9mD;u-EOpRwS=gv5UB@PLP{S%N@FPsKgefR*J36(2~Q-_$$(Jd_FG zqCilD_j`>+PkiH}X01Y%T7Ju|iN8KzoO63Ksr4r^oXb{bPLAP?sW?))wc^6>1A@Lu zGEUvQ&P>0rRb(Lim0AW)%JVSGg%nt4wrMldGrf`vbj20wel)5`jj<$Wv)!7p*MpZS zU`#F@Rr-oIqJG?W2jAaDBl0Bw8E36xO_Va{Ed~9E-~k|u?RPDk9gvN*{nQb2j7EJ( z%kA)R!(NGZdUTY}t+Uy&NFHRFiWYE% z%4cF~J&uyc|Wh;dspJp{1!UYC{;7Un)*dJOhfij#m>$9PBeT@!ErbMcnZlnclH~9 zQA}S1mV@Ogn;G6M;BR=K3nkqbI`Fm0U{?8(+j??I1s#0ABE28*#eRi0UbFubp|%5deHOJn=|9^ zWQ0ekVn5}pPAIyU1!E3g-@DLQ53%r0u92jTJa^UelZ9nikK{VsTnuL7T%@uccO>M1 zS(&9_-m35yQs{8C=P09cC2yrmcCPIWAyM<-O(6*sw!!M!o;8XwP#Eh&D_GjW+{C=5 zpXKQF=~lQ}(qLi5JZa@d5l34YyN?2Rp@cEnvD14EtbE+tO0(dM|y0|Ke^9l>U)(5&BK6C1D%#>R2dbSd3kSaNS_<37Gs|JR#V5c z&a5gg!=-prsge$y?dfcc5D@QlD{cXh%kB9PszEeg4W?G)Wuo6ixSLOy1m3>ouQ`)v zQ^(uxS}vz4G`sbwr@rVRn3;I9Is1}(cI9V2qM~%~ywo^S)yUIaluzpUacNJY6qJY9 z-qUB0n(BJ~boTzTibD+eAH^2R9YCiTx12z2lLY@b^W*j2kSX4Ka6+Jj#j zE`YVHrHAHU2vrFwT?z_pf~#xdTnw!|E`>Q(I%$95&$lC4%21Zp~lqeM&XrS%ZDtBN8m*zn>KwZ zU4p>^Z(YknG{YBrl9CmN$n&5s`2HGz(h9qtKkIYD}U)0}FH2?T3%)a`m@ zckB3-qy#Mzh0H>m@%CK0%wp8WxT%TVk?~ROcBa9YgZYJHN$n15cg4v2Dy^Fg?;N>O0oD?j7gr)*OvuBpJiYq>X!y;-AOu|2vckm)0 z^wY?|v$cHcb9KDN0wE)&DL-Q5Z=P5K_bs2eI}YAc(*5|s`IbK-P}g#h@+iX2ifJrq zJ%&YU)A${CLTy{1Oacy-(dSwF0pTt(_;o)(7RGdvHR9Ih<`v_5_bSG6J39|D8**=H zEm~!G!i`?KbnovR@x&t%$DX zS`=bY!TLPT4i!QzyXf+9HUP-+MjVHb9N!YK#fcH=T&^jHqGL-71@7V-fq3 zGad!L54ObsVrnvmR~;RUWGZj-3dOioE|dA9zwDm_JdyK}@A*GF(*b~>O786Z#(%C) z%HCC!foJ7NI34%8`V4}g-xQa@w=vD&b;n4@R#VIFa3Z9>LmPKb|3?Q=@v~N0J{G-9 z`8?R3AT~B5I`VWpdt+Quxnr5?;nDFBcLQBhb>!@FLdk3*c$dWxAPyth=JxNX8qE=? zR1G4U{cZL1yt+=O`BD*97KD`S#cO7Xg`;W3qg&rp&MtQxcyLwCCS*@_mot<%sX@n0 z3p&Lf3N$<>zLjFS-TgPDa=L2>yGbJg*AiTkzkY3)iCuCSV32=uX&Jnt53zF$abO$n5rj&c?n^8lhB;B{>t3^`CJjG@!r*8(fO{PMd8)0RJJMyi6*((OUG^l z)pYqOOFO3pOb?ccL3WLSLap@{K;39!@8!?P`o?T8v%Z+e(q>@WUBwA8*xJpINn+Y>==kyttGev&i&x=T;aew% zzZDRXSyv!UN&HxaeNV)Uu<JsA}P~FVz96o4Vw3!QN2#yHAwF<>-Q)JH~f^>I#%UO78D|9T)@CCs zzmTCtxJ=FK#DOJWf1eQ@4I_`jeuj7Xp@}^#5od_DG*V=uv>t3ENv&O=U7|(d#*WB3 zsm~vWwrbDGVDW-ZGpW(w_)@O@n2ir z1wu#wIVL{1_zub;=E{HUsXZyvfR+K%WR`tV+k!p?+uBBr7D@Sf31O@`#xZP{eiV#s zp7SRMCT!eggGKWn?2pffvKJgZc|l@^Xw`c60AgeJ+1>He*4puHUcH4-Yr)%NCqhxP z<`LHbXc$*3^nq-D0}!_raiK;0gu^R@dRmdIyaKrJOS$faFsSDR#WR~|2wWEha($cm zBvcLub`(4JX>u0hqO7Aq(pO>uA`40pBTI<)um2k*nNjR`1mskZvJyubuuRB11 zQcIUQr3Y-IKvrdttqQ*-kpa+@wzO@@z3Ia`g*gF>A#?3q%8XBK0Wqk&EROaJ79jBi z6;Tuco~PMBXaB|e@#aaR@QkwlL)+)A%OVy{gMQCzRU;zH?5U>~bdusF87f!P9(89N z2+OvB+yi&EH$wn>_=-OBj!YS+iOKOPw?@7<$?-{J`E=h`-p6U>inNLG(^UG3bNUt= zox?cYv&Z?BX3Wnj;ui&0wL=*Q*Kj6!ZDka`k7;F@-v%URrvLmz4NIg|Meh`(yoT5% zd(MBVt1A=>ZzsMK1QxB=zg=%1%RHmDztjg$SC{GQ_dnKRqViXe7N~{Uiq1$G%vMqf zzXtqnD|RqjQ7+PZaSN(Ty(DS^=pGQ#SDEpwH;9+ul<$gJD#WuN5^)X&f8CIiwu;W; zEle8r6(Z$7p>H2Nxg<_I9DF!;F)?iTd5lq(*U#qz#RL1RcHYfXjG82{#6?zMWQ;g5 zIG?K80eK#qEZ^3{P(D%s8P@509%qA}Dhlfn#7MxN-n?(crzjov%O!2?GJEFU1n%X2 zrXum7H;&TE!O55h6gKaCTXzi_N78vI86~u?w@-a|`D;&-l|1djJXOleojmHQtE?;6 z_h>3zziONBJXO_vF3Uue;HYaXvV9p^K>VRprss+C4FFWukuZU@2ZQ+Zva!6%>)o-Z85^ zWHn?p4q3HDdt@zFr*T|uvsWyCw$-F)b|WP(oX$9rnNBNj5En7wRm!UB2Gy>sLxoA? z(Z4K`{DoXfG&uW!CSVEL}_3auKf4zP82SikLjb2Fjy4-sIxG=lZk!QV|}quEKNn83)PYT}a3 zodv&}3gOo=$zOmL13B1713h#ctqXrLsCR*JRz0I29t=VFt#`SYW}uDuSJFcNS`yS) zeroj~LK|>v-_sIX9>XwpzTYr*u>D-}FzFN7ePV`t_k+RQ*I6zbnG<&MM?#rza1#d1 zz5HiLC@CE}2nDalNMTtPfSL1~)R)OaAnz!}>Te(}*qHwVl_jMkhS6G3gi;Qwf#*X| zxmTa2!7#Xv-?8Qw4gr$=hr*A*%>KW z-5IgrcoQ2w@!k`?{V**ik=>_=p<&kZRGX+n@A+S*uZ&nNLxyW_{>v$T}5y(2HO*9 zRRi;2>*jycxp}>llbw)4CX&K%l;^G4{l}z)S7pgHIaWaErFX)?2t))8I0qU7V2umv zr;S@MqRtMIzqqW*V#5+G{C=3`NcpWVzpD6!YZHrz>EfKrf+!szlY!vIer#?P`*;w`4J{!J-h*$uKI++&&+<*g8tau*_m9LhS2 z_!C`lCJ-#*&(0uh3|SopvHrio=tv9z@OgI4C7JmZToQo`r|7B#GZE{KoNxor?~Jmb)YTX&m)3jQY@vRYdUI z!$C&>A3Ldf3fVK7d8 zRlwQ;W_>66+C0%@<*6;2dAE)=-!7x~1vk3t)18#p6A-Sc;JPwXzw_=IY)mTn;O&WQomFIcTXZ;p4UZ;2= zyHe6i>Kuemm^zwsqTz<>nxTifDJhw$WcVFryVhBy(1T?IOKqZl z`{t;ILmiU!`KtxO{4KwxKG~3d`@sd5eW-?oPfEktOwu21L_z>r6M1e zIyrE0CBMbEg4c_POnCOX)jA-#a~Qwv$z|vfNTRyA6DO;=k-KV6!_99S&b3sl)RmSR z$=KVv*8^Dw3krimYul;PTA|Znu`J}fo5@#tg6>J4O2tIhZ5*LaAh)*PhX7T;EWsnI zU)0MtQqthL{Hy?0@4q!CrS((3E%oH3YN%~HJIPpN|4An8R~y6iGjw0o4PK#dRJtuY$j@gPg?_68DZmnqY{S9aER>1?ti2Niv_Q9bCtbnu zN6~yQiGb)n4!{pO87CdFPc@)JFie~Ost39Og#F)g0xdCzKxi1uuz1N*m<4%>*{!Bn z`W&u|bdGBrA~%hszumT-Br+lKaE!2}R2to$b=V!BOpu{-)WS`;nf5q&I?d`Hr=v=% z0v;_DQ^~c}Xxm2zboqUGG*>>_8-S5T!PA^y1ZKxE?9Lu%@@oPhEB9NzXN8s$Wjm9l zea2~S`JG)oPquoRE95LA^&PKw0hQcCQCL4kZr-86-l={;+ z$wJWXVvBcRa5$sIdtlMqeMI=oeR<@HeK*5-_>BK-aFKt-Gs<;0`nEzTX1ka9?Cy$V zTcjYNbw#5#A_>b04mXL+2@Vl>4KMd8@Ln#Z8#4B2BKXr@(m?Rh2k?BeU@qn3(RwtL zZ;G!qpjop5x!EXnC)|~Ab7+Zo0-k&$y*Y8-mCo{2Iq{80z1}klmfEQm{5e@yGA)!= z;fj#})rZk6(WX9g>!-+qZH-?^_?-$Q3J7^BiWg6N>DPb}52pmByMyNnJCl;--x%ve z^$%0@$SY@?77kKs_1iPIwV(X-li7Ei1`7|RJWT^D+qn=R%|QI`~L2S)-3gNqpFEVIKy z2|)&TFYqI5`fzlAziFU1pb#7z4K=bDd?cJ}y@N<9YjU`O?s>S%FMg7D4zjO|RFZ+r zD(TnT)6tN#JoD+>Hm@5->zNPM32V<$s$J2}bODSyt)J64;gEKu6~w71)~&i2B;$0t z7rTMXO1j;p_3tG3DwFE#`K-q%B)!n>%|m2L1$fIUALVAh8+9>Hg4)3m1EW!(X6;Gz zBSK%xzt+IIlF$Wb@9ZC^0Y!VU_y+zbjaXr?s$GVL`k6lq4STN2hVcgF&f`5iW`nl@ zxs9Y(Uj)ppIj!`B?8BRHgAv5f@~k$OSPQr1--fun?2)&Q8)FXMK&A%MpmdeCr z>1%Z(yr=>7y{Hr;dcgmU()~cCmgml4@42`PEi=(pv1w>s8o+J%otjoRO`Q=nUf|ye z^{FP-l~_G&mg4dMI7_I}D7(Aw-Wl5Zj%uy0?05c>n(N&L#bRR5i@}43&~E08OP-`L zO|SDkBVXB?q(*s^qnv_S- z!8(PXIo(HMh6XS^guBzC+2Y@j+1HAE9=9~QvbwO7b+*a=+4wQChlj>W!D8rosq%2A zrP6+if+`SN$MJoh)Ok(vu|AnP6mt!qGNbafvVO3a@VW$~fH@H?6wWZJakpOY@SI zHv1}i+}`=|3C|!vQCc$j$Z*?d%wjcqyf+c4NJ;OVz%^l$`xnrP719@d?&*(v%?29w ztZ0VSC>WhlaJR61|C+BtdlPvZBXnuV?oBM8tF7D$5}2jBG}I|N3~A zk-FG6xZ(Nfm`@-^4u#dlj|K~lotm2IEV8?GL~$eabd9l=yJjt~ht{PBNfCB%ATt!X zSYZfB0DLuQ7;+1v(<-0o@fyCU{Jp0F;iQd?jidE55h%${w5}|%kG0jsG8yX;-)GVN z8wnofW!sy*+Lg%o=W&gCcQUmfOjNyO6%_laOVz^+pXRj~x;Vb!`?<$^wK13FV`*=| zCM-|t4TN#^mH<{adkM0eedB}+Lc`XN1CCt6)^Ed&kI%19Wo7$DnHrw}f#*RHa;I)z z4o=O}g*6#5jF0lxGZ;Be{qLPDxR*ik_lWd+xH&1O6cst<*|+y^;VzIV$J+L@7-Y{j z<<#5tQjU%(Ko_|DEc@wN*E5crHm1@xQIM)JcK)5j=;PGW1H7Y)f#TAUGH=s@h=%t` z|F>Zakr8k8=~Zn;jOg;u_wiW)8dM9CZp`>I-pzQFxlK1(O4TS_EcZc zG0D!ba-{dx99497loft;1!yo&4dx0uezliHL#V82aa?&53pCCa9M4}gl^zRSF4x3P zRX|S;_#I1=&TeZbFWvP&;}Qajj@ILi{=~}(_7JTOCpB_m3L*^+1*5f<-uR#hdHO>X zr?;4s74Dq!{3^=v9nGATwrlDsysjU_j3u^3KISgoWet?Nuw1{WYQ8v^*jWKRB*$jA z@!LgsCx$AE)YcWO%^mr9GF|TVu7hp{^KW|L{#*ak?yoT4^Msi{%pkZBi@(>gv z2YVfC({OCn>}Lk8d6-(q2xdKfrEP?(&@SW!djns& z%$q~2fZVR;gk!|I*q_}e>uDp5Re1cl*`-Vv{h*U8&_2D^ymc%f|1f67_RR9Z#>5D`U9DBq9&(ZZk}6gCW_(?(md4h@nFLL2~EeN z7f$suY2Oj_Bu3yKn(?06HJrk>nCxq+Jvq3=&A)H44S%ilh1JH7OiegKh8FcOa!!oI z`)E1D^9d~Zs<;X^fz;O1w36RxX$YEb&XTGuP7GHxuxcf1-%AQsMOw_f+_$vnjM$i6 z#FdkvL*J0!p3^WVta&^1U2&;1jOFs2(ZXWG`A97jLN*i^%V{A)^n^zd58`p8^HFGi ztcBk>ub|U3W{$&%+@d0B)gF%c={=Nhu!yyMXt86KdM`IS867w$8cC6Stn~Y=xgzY} zX+Ym9TgClK@Yv;E!<)SZKTUwlkw#DqF1E{Noodnn40NA<4|LW91`5(o5EGOslVZR$ ziuoh58l#yN`F(p>I>l36Gw~Gsi+au9LgWAQUjYBgcPwCJ-pKHmVqr*vkq;H+vUr+yl8iMfX&6(i=^Am?e;98cRL`-55Nl|6QdpaCn2 zbZ#?b$`aGV0gRL%0WA9H=(zECk9I(>2+R^Yjm6>S!Jj$yFKqLlH-|O;U0}&^w439= zCcppJ>!tF$)ovmAyD~8ibUZ~v`G%dfm$I<8)u3W}8qMogG=1BwC+n}0wD5|11rWr^ zH+l)Jf7oZ{?6bsnPSA)0yM5s0a`1BG0?4>lvo|7uoyrUc<1yO`%fPNy@vt{`)@BXHTbV1@$y%Nvebu@bMH>q zx?%Ie+=D+dpc`0bN|W*CG%LGJVNy|yb5!XvY?=d1VUwE$JaPK$v#^iJLDlqmOn%Q7 zrg6NrHRBxM*M0n5hz`nMu;- zixqTcm+8Zr!1=nkN7S&LbA%5gnn}putqt+yiwDBYTG23nrFq*#3;NcCpaYow-IRVt zvKYwxCS5T&ajb7gU;X6i8PROW9F$;hHDc1+CDJ$LbIs)P&({U&g;is)6Uo6*e+mAy zEU!${rtGZblC6b1v1+w;0;Xni2j|lsJ7CfxrUG69@@IXO<6*Z0-fnGZ-SD*rasnhzLzZrZCg-Mv)&rWs51m5IgyVn{9wLQFk|8)!e;``Y2(T^Dlb?7#`6XL0?a+bV< zALMin!OTKT70%$mj=@6ya*=6=FgHxQCeuyp`vKEU;2nO0>nh=SQGJ} z*k7H`d9=X9F4)SwETBSC56eV$w+u z#jd~V!R4RJZC{`=g#RI=<2?ZChnoN*1Cz5|2PQ8()&zmn*X_@sDLW%!tCJb;i zeTU8-O6GlgF&dirB?-q|i^+6DTNxz@F2^}A&3=dK-=f2vaIIa@5+A`>@k1xNY~&#B zoYSZM6B(VK3itsw|LfYmN_R6&Um^mr0eU=yd!cDDVy{3KgPY-`Sqn_V4EpWw1`VB^ z<~O1w$s~ za%i!H0xyH}v~v6+bS1H0f<-7)@#W%>ASgIiFT~%AX3C18R2PXScEdV3JSB)IF=EWv zz1x|LNIg8-wcE+Stk*4;uDYUK_6`hnSvKI+Wmo6Whg{b@g>&u*tTnrBslvn24U!06pAy zP`|@6m&FU|z0A5&W5E~!yaiJs1!?HEo)9Any*RjZ?B}ET*x;ssNAv$Gz~%gBr&o;V zOGSnA#FTXrz@c>MJ|2Y8#?G)x^FNfQ8&L?ZLub%o(+&X@^c0zxa86C-RAr)a734H| z$V4O!iklh~r#M`3*AN5V6s1pQDrll8aG?JI&-IL9>7ihUJOn=gU42p|o2#`>`SbT& z47Ia%tZ`!8Zk6G$g;P;+tV6P1Z9JV^(IYPv75tn1_|HN z7X`=t91pZWvy4v_;*ZwPKOy!%fANvV1hg2nGUF0VnAEZUrVi8p@yh>LIPf2>9E^2v z;-;|geF&<_EEdcKeB$*_cPMmzNt{c0iUYIM(F8EL`4-9y`v=cf0hBf&_+fBGc~*x7 z)bHRy$tnbiXDNV)1(xFP0So~!1097w`A1YxIboh+7a3;OU(!IWIK})mH25nxD%*d4 z=znQs;J5*PbCff^RW2uMUCkPH}|Hi^1>s^^-JRRNsfKs<#gNG`g))L1j2M% zlNC?QTLyXYXX~a8{<|UxR(lf_>Wy5A%IMz-7Wtln!5D6>v(zl$i9F#u&nm(5CH0CEG3!++a|f-;`3C%LmihZ=67<5s4lbm-(1o2n~2 z9t&XSdfF%`sc09>10d2L6;ZX~z3}9w$#_X%oQqrU2?E30f_b*=8e;?1b-6No=W5r$ z%ln}`3#yrk>DipxRSR4Nj-3Wr%T$7z3^ZKNz# zEJMK|j-5QGZeisTJO$}7oWs=kFdkxInl2g|k=4$wE_vq+cu90eXljS8?9W%ZA(KG- zh!kZ&0+K73m`fFgn4n^hCWE<8WZ+2+&gXw?VjB=CubGoPp8UNo#tmV~%}#yP@!3VE z@2jt@QC79>8sgD7o}|%1`9M_g5voN$(sAh~my$~S_Dpoe?M;_E(zA8a&Y06^1FvIb}1jCi87WVc}8ddPOO*X~N zHP7>1$1C;>da0*r*DxR9>o&8H;NU{=Hj zpUm;Yv2koO?QyVD-db_1pdMvB`c%33L!N!%TNXvakT` zEoOgoD-Izoq0P;<6-{OfK1O}ufDzP;WuMx|(juskhFEz2py$yE#V0Gu8ZyP>+>Xmo z*T^juKmcDsIzH^x?u;D{N;;?qAx0D>U@qS9N&ffhTg>j4hJ$^cVE6hQN^koo`P6-m zAmiMvUgT#@c~Y7epo#fVxd^f^F}7q12|ia7({m0$f8t({crOvJ>0(WKdMkPH;^!s1 z$cD61{GHto;gQIF{8sA+*yM`mjBblvq zNh5PNkr(;R3++UVp;Pxa`YLWL$c#pxIeed@$U(h|3zHBy zZ^)ox!NW_8e?pi(D1V}x$PDiOho0$Ob~wT&->GkV#j?(Y+C=Y?X6%x5W_+!#yVA6@ zs>ULB=UVw+D*5qS<;JgVPHF2;szRe8$7$F_RM5BZ`fB6&H3GEUYmaXHGB!>{GJp>K zcJEeQTH6s8QKdrqk{810GKKWccAtIM=i|dtb7>VbJVuzCdLeGv?{pPN0ud{hS~#4N zs~wZ^9pHsBJK#YaKPz<7?&B_7xEf=T-YK_l?X8o$^;wLC#l^9%W88L`Z6oKQPZ7*d z>q})fi_9A_+fb5%z-9o)h&UXe4kDXSxcAwh7sr%ZA370SznEGm={NK?7QC4Iv@foM zK7dV4OD#t-i#UZV1QF8Ir`5e2P;O60Al0R9Fl2lf*YTdU3%5jzE6*3rbFyN0v?72q zrS0ltbA?|p8{~D4 z4=}NV>#Vi+QO@keyTV#^n5%KBU=5(KJA7&&A1w0mq7w-P+Qnd(77 zh^vyO!CSMYcMT$kSMOy`a&@4=c(k*@jcDpVbaf1Gj)OY?v%Tg+j(iKGe8HT#3ldv? zNGkZ&fU$%7o^O|*F)w%t=2Kr&@bQqN*^V0gVDRzBbUF$V)|`zGaCavB)|eg|yOT;#bhwGIZn=ZihF z`2ER_wKzQ;H?<4i7Q{$LSMeD5$i_lX(IDAK!6&h3`W}{3LaBfWJzLo_E2lg0$J#D} zoB5noktAW%&Bkb3oHF&iV$2AwpdGxjeDIP4f|JuE@GP${OU$iq+5))O%9rgKO+2~T zb!AUhPp%@bs^55{55y_*DwOTz(lm1&e;LgbAqrt%wM%KLK)d?d6m>PD2JEaYlbAm; zu12j5a$)9OfCmWalCi%Zgs6QrEzj3v44dS{s(ySIT%zls@NOpLdsO62oZ<54u-t5YB~(?Cu`%ydBi)RFZB#C3H}QQ~ab=!r}D&KuF;(*ygOp52mxdc2XxF9?w!bwkkw2onHzHI)kPAb+`f8rJ!e5IwIw!u`@0!P7MoE(pv)#|h$=lnxfi!Ry^g!>nyY7Omv?k;@s68r{_Whm z{kK1Y4|kW8o7PQ@{U<}ilN?gEowpE-aviH=x@(bK(AK-A79f4M<^iE7=}2pns+aC@ z)j5sE;vbC3?B|$@F*0I4T;Q!yD_FKbwr855N+RW@=}9SAlRC(HV`b^UWdi&DdoDQz zSe9@oP|*k`n4q5wb@gFp3Y0gfdPfoykH0TWDOlz5h#wbW0M~SZ$h-D2q{;;!0B;sv z*-z9HMAv?9CyGw00MvWd`84{r?eZsBDG_t2Dnk!R4En+7+yArFd!qqNyNv#pb`eDv z)o(vwD_N!m1~7<|J|IYw64p2JIG7SQxdwV2^@g;PZi#Du*fuyL#IXT$J^X7TUjbou zbuXTw7!+{K-rAw&uovy8JMRP}lX>#AaA{+>J(5j(_P-aMCV_W3p2jsy_qb=SNF&z% zH1tDVnOYe#p#oEa$i2}s_I<$~)-<8@ z*|xw|HY0PRjF-d^)becc>=4y(bW~_IymPt^@Snn)@LR7*=THLZ_ z;mR!*iw6aN?_vnN0f-xQ#kR`y`ZGb~QQZBdhzhTpF*{EOy$uhp_vVRW)9(0-1H%h0 zx~+?VlNb;2P%X#XuU=yf>Zwj7X_GaPIw&g(EgnnfeC-|rz6&CAK;u_0RNCZT*=~Nh zop#(UG^A~L*GMHQWlhZ@;Rw!!B@$o3zOzfW(^-+(o!bCm%pdMgVsZ9y2FLTnizs%p zh1?ItqU9AkkJebcBfK#?(Po&Am(6r{@;Gt3WbmjQTwO%2BK@)ZgfHxdrN zz}J*z_rF=SN^P6!7T1DyJ4LDC>#G$X242KGTdR6o3Hk8sVtTvq=yZyzhzoITYSPiY z@93sy{iPq2-R(JXr|?9A(Zcz4rZ|s}e9M^ zYt(zy2>ulU+e3GsnL`6O1T$ejs>Ltn4-&>mUQ5jgBXXS+3$l9g;+T}mJA<(N?YZ)JcE#diap24fwWaWqG1cJBq2QkI z&$$OZkvlAA@YZ-Uve)>{jot>^nR>vXLAzc+%Z&T+)y|z|c}#kCRX3-=@iO8HgAbDW zN8HsD;dyVZA^Wr+Bt9PdHXt3yRzse*!Np7%3y1xV@!DnhhFS%)O%CcW6#Kh@5GxA3 z7%ZZSj~1e~3uBEIZctRe0r>=f)H9)clZCy*4pv%Xm;v!&nZ}KMfQOiAqI)?ZgXoP} z3Ju!oqn(C2rH>+aXZ*CDX6jH)^s9zEk`D~M+Wo|oFv)RZj!UJ4 z4833M+wkk@8tz>*Sy}c@tX=8|jvd&`fc!far3i&h6d2#nbOZJ8O-Y zRtgv`4~G&`B_HjSYZFn%=w&u}#IPUkG2a>0JX6D{5}ORT7Z^}NEPChMKwQzMGJHc} zd+R2Z#Tc6}TKFxCiDzI^xvBK?jm01WX~1L1gX+~CU)dHOA|=^6TZ;PW|f!PEv#q@WLZ2Ri+`tiMCzZ)FkW`Hj=Qf{sqH7y5G=&$j z6XU0!=gC?T8u~eSvn*D*hcU_Z73l+sF-{yr0_T_(V7AR9+3;%N{Ql7~n+}0SY@PGL zyM2xZ?>7&FYN;flsBr&aNZ#uN0&gT>KLpONp1G(ZA>S})5XLM*f!+?pEpCBJqZ4Yl?^=8a0Y z48`l%2U;gCj>6}dgyE^TT4Rv>L>sdnI&3!(7Z`yl(%HmLPw$8&E3cHk-(|iSmCiFV~dm_ zwU*KF4UbMMZ$y8%@`O}4MZF)1AbW9b*?ztXlt$Pr82X78nIs4V2_$-7W%%#D9AREq zKjFQf6Z4pjOrIuDqo9pSA~bNM;-cxnQGVA>sdBRgW90!^FDKSD>-oaC@>?+jqxOT6 z;I}9Ei-CNK^fSoBXd|AZRzpclMa2*H$@U3nTlyVaP4`chZf`qchfbAdhXn+xVuQ!? zfZYzW6uuLt9bBBI>cm>GA|tC!65bbR3tuE&KbSFj`l`XtC2j2#c)AdfiENEN;ey{MYarEJ~~4yCI4!eS*v(E0fqyt2q!N!ufB z6g-SKNB3h|03dy6)|Zih6zwg<+)ku(8iD13Mw*Sz;ri~&Bm49w^Uqv!&XYA$>cgMq5d{tw~^k!&Y6f ze1w3pIb@a;cH8(W&c_UgVu4pF&S}qp0H-g@Zego}>p>+Eg^Y35 zo+zPXnd-<35q1lP-)TEh5(hdUd_gi>kYJ2+~!ePP1+qEM*y+Rv1# zw@MGd$38r5>;S^WeizC~opt;@VY3j{Q=UcMUQTmdkt(+BK=#YHogM;n?&ZtYy*O#o zxyh$kXzbR2gTEGC>-5%-=EQ8pEiPhz+1rc-pM^)tEop+D6pI_tXK^*QN6*MHPf|uiXpM^N~ z#F{^Q1m`NKJuNdCrPL&+_x0Y5@8JtP7#YA}I_qIQ^Er$R4hi(`@YhRun{zytC_F7p zRtX@rxF+QPc-8Z>Jd*tZLQ155?hGdfqqvf4sex=Fnb1Ca9+k`Zjt)9+CjqA>R_n^C zyp>~07zmdnhXi@pjCcec`iW_q%a_EF+hSy3LXO!L_05btEymxBGuJpXC>RzhqLYNTWo*xN1(Xu|@@?$vIwk3t z)cxdE-O;U~fUDbmWe&*QwvV1|T==0k!WsVQLvOc2)CaH=YAZ zYn(6|c0V~$1*Yr8pbTf~A(*o#S`9Lk_0k6oO4TFD;tzZs*U$r~7kckmB@%hN#x${g zv$-J}dAvKq_B2!M%ct?EQlgwEgaJS9PLFn0TQ256U8c|o_HclAxxX>`VVw?ZX%L8IG0kf||&DOlsz z^?b6p+3j(7s+dw?0WJM8F*PPP`2P%;j0)1I@Dy@|z5MqhkZ_Ze8?FKz#;LjGqc$EZ z-j#IAe2(GoycOFobh;_63#f=_=RO#zqcvrhzngnxURu}iBj!iy*`^-G_|t})tbw-# z8knf3Z97%D-4Dd7nqPIP?brhj%oFV9SAIKHE0N#KncXcD+1!caMVa-jZkif*NtP+s z9Rr4k9&qK0ZETi0b9-t2oQ5S2vlp=B;UJ@CQMZ49dF5b6&*5OA@0^ZoNe0_33_p(RSS$<=M1%&8`L$Y6sg^n+lJ!yLZ&y z3d6Sp97Mta-$g;&lmsmFH4fV2-8CK+kFAK@Zfq7y&lkFE4J9s2YgsWk74fWzMO(g_8dC1INbg5LDJVdkIDp>TTRZ1f-oUToSaX@iJKiaoudx~ zq%@j>w$rI)H-UPx%$2Ud+&1=ushX9(0b8aXW+gchZnxK6gn9(G*C&YP&Xdf;PAmKl zU*}!q^R<&9*@vxjRN_2#(>=+#%3>f)%1wpS{9cV3yu3+F~K& z6o%OTRNWOk30WkQXVapU06M#Txnb|)>NnE|9uWpHe0y`-A74Gm5y9QMsnATm>*)&2 zDW^iCVe`_-|HU|uuEqKg?YsnrviHN5z$bro?bLo$R7I){aW z2-Zj<0zi>ZGOFuj7la@BgcibahIaCMui|9&PLMa;r3##WoGGK6f#ro##!1frN@Bk7r&w9z`o6>P&dK`@2byu%=DI;^7(fi$IuKI_IB(EZJH? zyOIZn!V=?g*u`9kDPcz==?U4f9Fn9RX_Fo=UL;;wvLrnNc(%W^^nJ*YzQJ8Hw@SoO z8eQJ$Ejzq8y*cWfA&OAKU~t{(9TFBqUY6>Vj8|OAv`3NmcyJroml^lXBZOz;^F{Z< z7JYB*(MNmdejUhlAD#p0yXT&i%x|729vg5awy6>n$RLG6e<3O_KtyGc$kC>?*|LfJ z!yCOHYA3*6vCI=VO4LdO@_|R)Z#t@Ix{sbES967H;dnUBF$D+oIFf?#)H~ zaECB{e&DU-@I}ZYZmv}erQ1JzIoVoXb{OeoIQXWwAjraUP)TSOI6^uWsx;suGnJI_ zk2k~LN}Ern=A|2>Q1yGcSHQT&ur6Qyh>-rrN1j>WYdV4F?Gc5uvE^G=g00TpDjdWo zjDzXd5C;}M+DW;hMgb`fyKW1J7l%aJ~?|@X0FHVZ7H8`7~(?7iLpFdUUs*3 z&}-?k6pR)TYNCW~9Ft(JRF=)x)#6@O)|{8zS7??#Xi;NK;dT33ckY^&=jct~(mRHA)MCj+o zS?+wH0?`fhA(L}bu{5hBjXQlGuU38qF3S*&6>yIN{nN~j^^B30MM_`a!AKkk5E<`qe@fk9*a)$}bMXx{Z z4_FEo=0IJ$J*{C1HfTGBS{&t@Xe|qV?ILTw2VvVh`E*SLahE%w_i#dH!%XQ)gMcXL}QDy4PhPym;MLh=3?4hqYZdQf8jNBOZhAi$-rN!N(zIHP_z7>jo$XDg?#Oj9h z?`(SO057Bq6NuRB@(~GY^WXBGiNH1-kZh4LUFhmc2yYV#76|%qIaL4kH*(6fkG1kNb`(WL_xspRzm1!c!m;n(#$3W{Dtw%HD$AAbvrf>J}nLHO$x?U zmz?`O#AGS_ke45+``_J_Prtp`v@iC;egfv5p#Si9xA~8#}v}Xa?!$GJ=LpN zwNn>!?C-I4Te^goPv;KjPRvVlT{`B4 zxnSMAF$PbwZ9|CG)9^YA@gIbVU0hY@vpr)dj=t6`rHJCb^FVxfQM&p3v~NJJ`XF3k zXR``_n@N37M@!`r9n1|WrtUMbz`9Y%%?QcR8w&`MrW{6 z#ky1=6cwiwz!YgQT1)rsOe^>Taq&!=J_2ETXom(<57-=Inl;L(1`&QSriX;2ZEJfe zLCc11l9?tgLjv#|11WmjyFE2l+%otS5F80Tm-8GbRyT965O1w78sTVVDA=6-6eH(@ zw?&}8bfk0!G#F}=mJBx9u*MOWRV;c&FE&|AUH~(>fyG)Rij4c)t7NJ&VsQ_muq^Cay^O(_+aw1rQMm3hltCB)Ou zsR+F|8|racS6Ogmg-8ayvrl}vv~zWJgBPNO8^yz)OSyREYZLeG<^=lEM+Igye3w|! zfH+(j?7066)9f03u%ecV`ukI#8jKp`$P^0F!w)Hk@ks`m;z3Nf|B{MsLsz`Qbm@eH zA*r&`nSqt1W?aWy^orQ$7-(}gl|Un)+_I<2)&&~g6*8~9w9{*dPfQ?W=Y06J$NS&T5+o6Is64K9U{pQVl^ABg z2G0Aner7#0*w}j{SlVbBD#V*&vkXQ=VP%nVD^#itMBOkK*FRR}8X|32Km3*oU>}9lKBUmv`d-a~{6>|P;_l0l+ z@WWH)?FTVb4sNB?%V*?iUUudt^V1hM0e~1PbW+dZvff#q)aMk$l9>_TD@Y%M>sT2o zZx(e`NIq@k;~bnT4;*$n=KbtF5EO++D73qgJ!6HExGLpjX>^2uqbCJbo@5qIv+M6VsMKtMW_4yB|;I&VP)q>&hq zMi4~08xbTV29cC*X=#T1t#R*jww`mI_kDhE{WG5t@444n*LAI{*LRtV<=Z~2bb^>}lIwhvApnGz5NP>bVdE!uZ=(ANSnT>(BXC8gtrzY1o*mPX1 zxn?Q4Q=9oqs8|y)TuE{#%$udI{^?MX3JYt-6k9R$t(7_Dz;{ zI>WE0#IsSY3T$|RcsUHa5fYAEEjP}!l!U#MMNVu=eV^{H9xio(0(mCLZ7b!!1t{DovaUQPTPjZ?i8 z6iN}Zy$`Kg#>1RF&K}pJqcK9JW?S5G~#z(xT*Kbglo6a@Mvzl>P4n+50s6Z@pXbs&m=3hUX!FSlB5 zgy`|o2fn0t#L(M|RzYw!We2YwyGr|q^xusOWk44S2;O%`%pDApH5YqAj%oBHuRrMw zCzv*|^xfK!GNKdqsi6CwFX{IMd2`OtjdodfDqx)Mm5y(LC1N6o@Y|M(Onj)N-8c)R zSN6pQQ|OWW5AYq$O89rvA?GeNVLR>dV9gjC{C7=~m7sW>WEdi2*%EKsb^hjorK&mW zM@?hhpb5QoB{z(FYLGPSB0+s;`F2>CBa^5Q$vz^kVxG^Ni3_U%sd>Ymxt^J!LuLgWkz?;o;~ZG@>Ks3-NTtU)BL%LO2ZYig>t@+Nw~p&%`t#`6!URnRIB-z zKgjF2Smtj&qv(nF2*RQm$M8PC^aNdVaFGZvrX?ZX}~#ef{FhWJ-yPiv9$FKEuF z3Ez7plbsCR9)?OJf*0c}m^?#ZP)PV=bwG`8elP9Ui-beCTlWi?tIQHcfs0>~B8 z?i;yB;iW3}igF^tH0KP3k_qO9-bdLpy=IO%bxa>leX%1+iLeu5K8#PY>at@hEtw** zI2rv1xXS$-Jdo;9G7?Q5jWLT|oSnM<)qUjynm0u0Z$ptpMC1X9dLNTOj;Hj;SG386 zI+_*>Q~;4-1a=UX3h(-QK|UdyGRf?u z-pa{)7*%5^w(lOC6REijx{xwTecqId*P6favFTo2TJxTpAccL6uL23HzEkmMOoHC$ zkRzL(Gdb1rq3uEMe4}x0N)p~wolT(WOT#$zoGek$dlWlkNj_Ord+MhxRZQ>UfV6BG z2(?6ZJ#%<^R=dqJpow2Z2yf!==;Sm{SS`CL!VzVcnM#p^o#4i=BvJ%|g)C7q+%7Ym zOJ)W}>A2=s-)b+owaxg&(5WW_3T=32u`yv1L!T@NWkRnlA`!Id7cY$?71p49)K2BXX5BO8u17h1p%4 zSp-yXD-&_%l!KpqAjkEg{f#G5giglkG30{=ma^Wy`fqa-4tuU*?^ECdfrmP!byr6@ z@wS>8JRA16q>K1RMFyu$`~jNH<5(^Be20}U3gOTa9~&{aF~R&|u2eB20qEZR#Xr8g zp@0e)K$FAYC&>QNy;zEJJjG+&#orP>)zs=>)^?X>_aT}M=|TRmnr<{F>u1AHl(w?V z8*xa&;n`KYq!qNQxYxGn7;9Cj-w~R4+6H1Hy)C)Rt3F-oWa;qwjGGyK2$KHmmjtXV znL5d6jl}c^UgdLl4r))tdOwlzFTK zqPBqm%NFytQDG~+u|XSfzGSEMsnlAOIn*5hHo*JI;6lY$ZGj%h({?8`?C3tTZygiH zD*Ow@^OrjJ97jaek#ybZLLFa_j|GW2ZyhST-`vVZ+*s=u zEZ*#u>}sc+XZC1PFNHi}9i&7zE{VWFaV`Qh7hxDkG+I=Abn2hx_Oz<%kcG~2N1|fF z7i8Zk`l||{9~*_WxdA18Td=I->d2^l@nQvN9k4cVjy5SIBvz<(L!aPW?P%hqkFxe>|{oqotZ|Qr6GY zjB#G!G>LV@g(~$)wj>@2;zLSbA{=yLkSIa!+eVcf3HL2;zZK{71l3H7X9kkS=H|tB z+@ckY3@E#4U(BO4_|vpE9T$OR7sf)__guH@(eChN>sn>yOShW>TkW$}W1o$k$4s2c zEos}p`uyVcVhp~og&iiMAm_qy!z~Jqxjtad+VkUV%12y20os&e|60AdtE)en7SSmh z^nj)I&gq3{M2pIPe1wzCVb?@%xy*=X;l4 zAM=RW?&s8XIN1N0Tv)h882OhB6N2c$%VeQ?h1MPJw%f+hogO^4dB#=Q$+$?aAWaN| z;zG4w1~TvWZ$8o>bOI@R!Z^?C1R)eariieJe|GE8gZNB!JjoLim#)YQm`jYLPu#?! zEVvFUlNy_#=@7F7l&`waOp18VgOnBEuGUP6%I$pHL5aw>kKV-HKze-s&faa9!o|UV zfKzppoPo21_HE}<$KuHD10@Ye@!kChw&hAf1tLyC6qt|W)cLV*#+Rq}T;T4Ynd|!6 zVrl(uW+}gn3LUPwleCroA*$-h+|WBmgqKP<Wm)X* z+1V6i%(2jKl#VeNADbRf2?>i0=2>Tg3@fINs2(jYo1<4c+taXX$MD2+b zJKvf6cwynwT9#=F)g+&`VWUw5C4oJ%Tgt|%mXy4uWZDLplUfIH{7lwkB@b7 z@6%;Zj?&?#{%)9$j~U=&4)58(boXnHo}Kx&%g%Jqu6|!OlKwCWqkGZEV*^|b-Y4Wi z_{Jx~^D*f+tEt`0h_7!AwVA%cm@l!YrrKhMM4OaQZ(3%bGbfhirWV!V&^6}`6pPoHHa7l73 zY8uNul9OnH#-JYErV&nD;5X}LeH|wYwflq@w2m=C7~U!ptk<(R+A5fEQZFxrSaU@3 z+bu7-%ht@P8(CdD#O2DTv{nw6bRQDSFJ1nK^568&MNBM~#5*+a;iGO`#CI)gjphs%4N{z(eWS+~T zVbMa(>1<}=en7C3rKgLzi5WH%(Q<1ws8PS_zExdT^@%cW#J5Hoy4U?V+$_>=T-k4; z+2-<9NBkY(+si)BgeqLMqJmYe?kPoJ{{UwAO2SK#YKj=A{DKOvyIyX~H$o^RAU@0YQMu(2D0tdFaF+iy}FfO<2^P85k^+o!eG zUd3zwG5(e(!Dy`ZfnfBPOGy$v0oZvbR$Opbc>v@yDfV4F$S(bTxH za(8L8uauIofya2ZwkNZgKX+KAq@8llOu=ob=4F9}qp_%ANkz7E?VV*`(u2l>Gh{$c zPY?8{d8>7LYO$ zG!K@{5%)MhyM(xVJZIIU&NoEVtx*fN)A4cHie^4s^0ercF3zc&e~8Krpt{fJtFX6T zM~A9QSykOIWMpx(w^yEXvrDuTK${mxXn|zhG_kBKTL@eo4FwHBS2?-T;~=le4s&j1 z>E1b&D<`0v?5>+s`NULitWUFv)!OtljTDpn_=4~rKIg1?!cU_UL!RrQ?T!wbvv^4^ zj>2-g9M{ck8oE3`QTI|09nr}k{%wf#=}aq0k_s0IZy%jfww2+{^3 ze}d0~bC3Npx9bVY$glLhgp?IMZhzoBn%sM?)?9T{cy6n2`s$y16lb66>{$7|Lk-VI zN0D7&Zb>ZpWK<=UWUvRM{A_~w5qDsfpw~)+OL_zuhsqs+6U+aE>;fl-f98(xUxlV9 znaxBzYykJLh<53!KKxp$HBjxz*pl3DNj?M>)tY)HS6$PLzzpfLlgD%5jfv4_F<#n` zTrcHGh?c&LqwJ9}V}DRDb<(aIfQI2%WHMP;IMaKyp2fe@vNSoofa&JBvh#2t$H_|? zZJR|$gf;`irT;==rY(B{)Dk?z)vvjjIFQ}~p;8k$Xu0ygYpSN2HgUSm5Y6YhHUCWj6X(}L45BEn)^r^6jVj4E= z&ng8WfoVp~d%MHs;`dilN5A8~fsV1;O}$%wh+3~2n)L`M9rj#)s&PW{RHRs)cQ`&S zUNJXlC7?^6D10AJ98}TofdZNz`EfGUMfa@ ze)b%}-|ii!e)31;_Gm!mO{)?5_tz7pYZ{zw**MwomG$44?%*Wt_FVO>A%Sj_d>^|Z z^Ar)+Njkj&H_F{9FmrK;bKvX+%^}$4SuL(Jtrd(pS-k!QEv+IQo2F_JTLev7tbLql zsOlbbTKDO*b+PCcl^DXO0?a2pUE=FoJ*F{FH&4P(#5fO+7W~Hz6?vj8DH1w zYW8fqh$V>vzp!sZf0ar~X&Y&~n1T>l`k9gN6Qg>wMVD6@lCI{ z_zhk2q37z9J-`67=5e=0Fe*#Sy~9#D-(0Xpp68kvB(?siZjM~zd9ME;9Gg|u8groa zn#4Dy?me$E7MAH~NV%8ub~NF1t!4I1-Lp9&9zMT2WB~$U-ag?&v<_2d=3BR0oL}G; zQ(H7v%u~s1iy8~O_AwSS<7{ybMKp_<8bv+1T3-?g1)Fgmx;OIbAwO#q{cOyfDfX71 zOA66)+Zbe>JTi*buZ}Db_4yd>)=OHI!lMjK^b<=fxn`(km&-irQzHW8a`dvOMrUe? z%h<-0=nju(kIpPopj90jhQpZz6?4;W<#}(@COcA_|1~A>Zy`x0d)E@Bm5Afd@d<0(eMtPdIvGw2sbahU0TkYtK>t=XF>Q=C1`vm#-H zG{6S0@q8v!;HqWz63dG6bm&ETa1`o51LxWPi7CU+0ODz8bpZBr$mo{tTZeC4$yc)ZU+9dLDs|Ta7WlB4NV0J4zi}o^I$KGqOl0HQFG54#4M&b6eX^d4l!?%vC$@ z9SjtEGJ0;#-Tt{r5!{>!w0*jo840_HT*l=*I*$>59$Kj{8 z1#Q0HoKdX`3Q4HIn}DHAYIHhWehE|g(J_0?b#c+EW+$GQmlu zxZ&^~ACIzBpdcxNbgnJE$=Ffhyy=Sho80yTVPi>NoSWthCyWcU5XO#Bo2U78Ay9ez zR5srkWN8vP;WvQ>VB zJ!XQkpUngYO*gdp!Ykr~qnxPww37PA@2+7{w@eA&P!O8J5b#qIzt5sAly^4?X49V>5)kHkK!jui^x&FiYhLFhY9B|xn;FS5 zNp$^~WqDuAE_H-semK}>lz3MZL%gwMOdWykgr$rq{$>*{%o=Sc+5E7;g#Pk)5J1r* z14qyn;wz`g$w!|eQVIjgH153$-->3c-rJn98RI`e*Q51gU^k616qCPyw z9#6Pj8d_?ohjh%4wB~^)X91NodYN~t?71PTNd!-;G;_!H!4}A#laJt(OKX0h?3AMQ zN&6yClh`6&y)=`(aTzp}V z{f3tjJ%r(??Okt)YAQxHy>JHoEwkBr{7}agP(Q-rPv@*ElQd!vCg$o`xa4@18Xfg5oXzqQ0g6q`aiP2(k!Aa%T^t0Z22ISz zIdA)5b-NgkylIRd`>ZElJ%Y6>QseI7J+F#9(2eX+Vmne9D|8An9C?%KbnWl~thO)l zU*R+_r!rUI+ zPUWaQ+hX#*LOV)$!B-FiSNsPv?RgmX8_RvI0%0cGKhV_hF;FVOC%UAdr6?toDKpx) z!{9BqIA{A!)1>`ZhL9Eg3vfc*mUDiHPV@_;d$+x5ov1IW3p-pUQTnka57SI3Q_3K3 z6?GraD>J0r)2a$(g0(hEGlr?nq79g&SecrVhGG&vj^jK}0?RE8UCwK-)*g8Bi%{)L zy*6RRe2Noci=ExGm)-UZmoifF*};Zbs^i|Z5aYy}yrO3#Qz_!}{(g_{1kfvJvnZlZ zKv)<;*{Jgn0{O6;*Qq+WqO;0#07GVV;y$8?n^UmofJY( zlPU43#3yYvVO#rMANgUk6~1@%UVi;bU!20{i-g)%LE?278rt5V zqa+Huu#2)I=AY)nqHsTcm#j5A%`QSu(n=1YTkjXQg$p-Mt6z=qB zC|oy9x++w15B44a?U+KSry{{OX;>5Zzel()AHZ77F++xh%BF7MT?aq@c)QdxGBXzJ z(l+)!g2z?WuL}qf08nWL1)Ge2Pj2)0`XPfqD*%OMy%IpAavu!EfU4yG6Y2I!+keIo z0uj+VJ@f@Y<7!mgjodG?27}qWt~uG&V1L>H+5p5HCc@CQZ-67&nlYhguUr}Yc|l_f zIO-*?i~)Yj?FIV}4K^rjD72rD;piyzpdSFN5#L*(0tNR~7-QCGi^6Pt&AfT;1DZxk z0g^ZrNqNYCMhun?PQX5DHcwTcQwH1T1@;YGEfgA6SQ;>0|C-=Of8Y;5kbc78f-ld| zeL#7%jfS8Q{7}wzL3isl0GZ2s`?6k&HiyA|9n{6<+Zz?6<-qz_iQkR#=&=xB5rP#w zgmtyrXafc;WU&Bbq<`Wav*G>J2mbfL0(emnkn-RQ9ojT_eQB^Phkpl2QT$}?PT}gs zVHy3aI6T_I-iM8fbdONOU$q~_Resk=J@78st*eI`voLjw(j{%rdbocQuF(o3gkpd# zW-aqE+x!lA6qxcKBIW`ZfHMPY#}@-%08SnAe9eaBKiUTXHzWd4Yu-a)9DB_v{mCeFY--h|$ef59q&CvhStp8iD{%^e*`cKeiKNA)IZ|DL2r=6xBrXufKdHJ|8GeBA1Mv}|J^YE=S1(P?L69p z46XU347#gIHBj(%c&tFX`@hOR+W?N3-&Ng90Iv`g1A!s~v~eJvmWs}RvU;-H?NcqN zPEpVs_bV)heGQ-5YlPUlzvtlL0?<_TOhm7~^Tx^PW$B1}Ps$++#_>j1O-W~#4;CE! zKZf@wv@cy+f&3owH`w6R`(#A`B?*uY0W4;!iCEsBNTDdC!zXr(T`NUtKID-K>g-oA z>e<9*|KDN@6f@jOMi1rZd4hW zjPh)MvIrdv8Ui*iL=I-5xLvm2_u#Wo<5_+14dkev#ac%wfV^$H?7vZMrKqS+%|!_i z1iMShP?W!ovhz3KVqE}-zY~l@L|(+_tqpvIJ8HjLaRs|%&fb`wc+tPd={OONHj|-X z9p4L&@EizGl4GZ&eGrcR$p6DTfOPoS(9Ayhe}JO-7l+Qd@Z;k;_iT4Q#N0-2Xoup zuyD*aN;vB6 zs@3I~MAeOz^>)MXuB6Q!=u;>P-!nuSKq`;9zc}Uzk;_B2KRoG}B&NnK4|rBmc8Vmx z|8i)bK&SiUYa+^|v47&=%4LWf zXQR)X$JU|1Lw5{1+83UFi-cM4Be$PCTyR3+TY^hi)*DJ+5D6Di#=AyU-gEbv&$y8` zcPUmkOl({Uc0*lJ`fxGn(n~>5TxWHnBnY_&T)V8BcB>Cw19p%8^yQ$xF0c4^ykr=2 zbfWKrTTPyQIoJe{g}chor6f+op6wshbNylx;E(y-EdGovL}Mg9D|3WTN~ z3!~7+r6`MqfpYQcsBSlq2Qm%X1OLX^LqW>_0}dKOy*L`+L4$$R3Cff9>A|gC0G(Ny z64t3&4WLuMRmdL({fVdRG^RQ^-*W(ds;y=^;@X>&XT4{Ue%%USn!ku;j*^4jL!rlL zi)1z5M7eX(zdDWo4u4JLA7tyC10l24GY)2d;!kJb`1`_zV;TICF3+~N|HmUVCy zQUh+DjejNqQWE{_xwP>((U@=y18AD}6I75k00gbLX1^g24#wL2HJUhXPv8RZwm&`M zpP&92*cGpxln301FE`SDDWknO!RaO24MOLMZ z8=}Ssst(MbuC29m2Rd`*Y7^}mJ^KT7)eFG$9=-=~f@?qg2EY98z~gcmpLAH5%(T+p z`_>Yoh%HO$KYwXpadam9@q3{)$U9Q{7>HFJtHJ70!Yk~bd{)cIkjnz#vrRky~*(kwJS350`POM8)hvy z{M^|n3~LpY=(!%*F8gFo&y5=8TnTXyBCZw`T)_?mcNJv)*a=6z*@pXvasL(#IoiR0 zIKpT+0MYwjfoPDc_4D1BKTr{i6cz_6M0@hjJO|K4KX=04mpj3WAHy)1D?ohrr}q8> zuzVEvKirf8I1Z1rgVD0lp>YAz@+YJ&K+|V9_)qr=HI-WYd8-mU+8Tx~A+3axq@NJL zsCey+?oV(pqz`fz_hly`x7#8&pusV|$5ByD{ds{*`GW+hW(`@d@BJiI{=ZBXbsg2M z3@_9i#r{=1dE%O%DAmR_Z$8oiS2f+pAomw}S-;;`BR1WisRyvdLKw#3w=H1AaCHmV zWz+|T#5q{V4kE+=pu3|?R*t_`C4hS}lwFcqyjUUU*`_pH|7-+QkpQDQvqj@(WIZLh zUse;atH*d{Xz*<(7~wx(b{WFM1GGkaICu93UIa?#n+*~b@#kb+Af|kujo*()&O?VP zuxyPSVYJ)yAqpBX8rtY`82B++&rES{5e$-3Gi7Md=~~`J!|~UU#lzFEhK1%`CDHzz zp}nG#O*lYDKh}J4>lC5ru-YhQHC>JvOFt7A@9UkkEp+#t>YcyJ=3`YD&@0L$wRp@~ zrb9yIK!ygBpo^CGYn>gN9pC@(fWiQeeKlV78)hs%j8sgw?wNWFPoOm73KsFcovm0o zUuIUoX6|AxwFu8rs?n30hv-;)&!kQE8V_5?f)OkJg#uM{+JhXa6}Yi6Xrv+~E@+ty zCRqMy#Ne%C!)}&lcjUTfC7IHn-jVfj6Mf4Y_lN%`@zKta-RW{&^{E^ODf?=U!#5n% z;NrSvVDRGSzYV_VA|)ubKO??_kZl|87LA`p$8rh&prU7Pa;9()!(abq!e7VJUUCtR z{T0TfHAPR^{HS>2=$p^yU3|WRkpl}U^vi5?+5!r^#XLsRd3h=(ooq-pG4q%lh`jl> zY{B4LYZaMi(t_w|5h0knoyAh(VyEA1;s(#zi@I|S6Xn;3;Qn7>w3myh%eHym!QXy` z(PKILeO^9cIC%s|fPZ0Z9*iAbg71HYG2o=6*V&sa2v>OOe}$vfT_P7ICg@}od(yGa zxcne5yDE-BdywRAtV{DVb&bQr5=}8Y_IzzIBAI7@`1f5gOiuT@E9<9V^}gg5AO@MJ zdXO~9cJM~|wiukxJ$IRieboiCzyu#HGb>aeOXu@nOhbdAf1`V1FKMAI>*pzZP$#5p z1@|AnL>xOqigD`}?C?PD{9vQ3)$yIG;>LT~ygc6o%T*ijjv@z>{ZLZ&d}B3jSulPi z4j6xDF=TxnpRaEI6KgE~C30C^RsZ#RguKwU;8-xNUf)Z0F~hvPvO2i-t|1JUVnk=a!Mh_T34>h&(nJf7nS+tU z8eb+LCqqp=lcd^M4kIv>{ac~~mr`Dyfmp5se47cK4g(8|7>Nf@*@jWYZekITo92P1 zg7hc6h=DNXKQ^nYd$HQaap|7|PaD1Szu^grSmF$cKQ+#{0R&C)yqLZTyg#3VoNZ}*xpNo z8HiC4s>)ZJUZ!Te*n&+zGS=wKNGZXu;<|fb4{)`oPy24s>3E><(K~c zxUv8Zqn`g`wLh1gz|MO{DZi1hZxPG*2vd8XIFXdkb3JgLWf;!32ooU-CyaVcrsrlz z`SzxqFt)jqoGg{#amaiz+;b5|dHE%Z%#rX9!!RPc@Am%I)8dwf(+#z;jAe~p-nnk@ zWj?O%vI{ba*_y3>c;6ySm@3_4@1b5oF2|Q)>-mbe`JvJzxy!!gQ53tyCMR(EB^VLf zYq-!7jFEoNAhtc+&#!NUA*^++d~IsVn#ReQWV`kYVPtbnwY!ZwoN5_H!}|#b$l=Z3 zxM*67{x;ob(4b6QDVM~)+K`ZZLeHO5%xw?9!?y|Exdao1<`>>Xz7#*vMA+p?QX=JK zrDQz}Nf6CWC1P$U11cZ|bv46)7rhmUy%t$v`U4v9erR!ID z%&P-38+C3fD=E68`+l`@xU5OMNzC!USw7-y50cdHJ{uSKOK|^u@m>m0c8JQspipcq zVq#~08*CJNuKhMPs~p|M7PVSris++3VTP!vxUy%8m|W4zPPPe)=`<1#cMzCxs_%eA z$1mlZ1=qUOLd2^!%&99)#1Ks)$DQ-cLsMwK2@uv^HW*c{YUu4{9-#X@_!$!zz~@-6h&RntkEJ_baGg4)N};{mnv zeZuPAOJ`^@}82 z1k%lXyTzfPIaJUJzj|5kyz7g+aud8|1tvyzwxm(tn+iYEk!@(`sMbF?XyWzyxNX7? zCt3v@u}tyUgEMqTtz^C8(6CACApSCP=x7!xa^M)xs(}AH4KpqQ)>BJYn-c@IoIe`R zU8I@3>Og7+;buijSOq!=ij{jP0EK>emIJLs%~H zV1YW7N~McHzT&M6x|3dn+Kjm+GyknYpGj9@fO!rEGjumm;J?3LKM;BUP|7(7an_Sh zd$OZN{r3*)C1YG{5r|bHs(&c zJnOmC-koJN^HCh|nl+dxB{>VtSx)DUSzZ2jA);hCL(xNI6Jv+p>S*#f{OUT45seMb zyAC6!vT8mbQ(3-F>vnIU)L?|jsG;A}bAgtM|KZMI82sTnjN)=P5gxlfGu(e2MoFFS z1GL?ONtF15ce1zbxfu}idt>Ek7YnQDXP*b*wd=4OXrb__br{)WWbeydd77=Q&(}Dc zzI*R^%>it?;n9^tqI17^7Iy|c{+-pcC-Y;nIai-d)DZQ;!{ zFVBvnJdJ@{rFw!Ig>Tyej~m>01Bep3Ws1)rDFcGpHCdiyd;H;8LsXGR8+Ns;!Nw}n zFWdz0ypIp|;60YXFAu?2Z98@GXkhQ2&MCEDDQwb(zg}0YTl7^{az;V>vI{FuyWN~OVOTuTL$=iZ(kW8 z=E9Tsm#IpZARkwKrZp|1Ux&xS`+gI=i`zEwX*U5LJG)ZV+ubX~LlxUqD!(WI|65o+ zBGz;6jC0VXp)p&(i-v-&l2Uo9!?o4HY=U1m1|D7i8Ia1!X&JG{VSvmAWGVxIt2)x^#+o@@J-yPZWeQ^JQb9KNT4&A9vDh8cfnj4O&UO1T@(!ZI+*}WyNvtP=~wQXapB3nzu#awb2 zqw3#$(3SaZWI#wEu+U1hFGP)W5LhdYQ9(Zq@R$d$=KL-$RzNd~TQ_ztr_kWn|y!S@X$sNTSoo05ELUoNrgKX*Fgp|z2KZ!*}oVV=AIIZJe zn1NW7$PKH#QGD9Y>&biT>LZpCzda5ND{z$=OfcXo!!Rmn?-lSaB5yGLNN&9NjSg5Y^eaH!j<1v zkRbzetecsoe&A`>C%jx3eRlfs(9y##_;U&8KenyppJR}fd{|FRg*YV-T+Cx|J=EdN ztlEQ%*R=iW0R_G>S*HQFUxQLIkPR7!E7vB8znveV`{tU~>sXC^qF7T59Igw zi-t+V$7(P$x;=J;fHx}SE&Gg@VK-RRA`tDxQ(dsny1?pQz3=Rn3Fu8KRQQ<*hnV<# z-#g^HbOhtY&xaYf-oU^DBo4P;z6iZ_tf<#;3n-qU6*)ES0p|+~J|d%}S8B+Q7u9{) znY!H+s6~@K&IPX%6YQ1C&;-xM+sGf1GHPpQ1Xn*q!tP?gGw3c=RsTT3;x9o!_o+?p zYK!mA@E0}~2|5&#)gE{1uTL`ns*U=&P+EC_&6R+x;+guFjIOJ?ufYKRTQYfhf4rvu z#bX$}Yr45!yZz%DE{}7wo(Ty@xG{Zufdup2qSC$oE=FNV0_U{Ua)4&c88*upEc_AT z;3#h1xjABd^uiifB@CK6LL?%OdOS9hma`mXO~MWB%?ql=Y)TJ=Y&%NuTQ5s2&t2|G z5@ex4t}$-!NvL&{Yo$k+3;KK_T{X6k9ePvAbp2e*^5Bu`ZH z#p%{IG=0(RaXgg^KbpE?_8 z$>uiEyWtf01{?MF*N?tX*9#fm(ANG=$oHEoECW=%K=uR)BX1nwlgvA#=OE>9ML_T0 zSCFBQm-jMOmGj%Kn75EBmi`ZjTsbdKlpDjv?c&>JX^X9pZkuH4-M#mSGj^JWr53b9 zi=JC%kD>?SmOkL{-S zTVaslNz|}1TXekY!=2xCu`5t7`CoD3@gA200ZP6Ntx2W=LE-_F|2ILJRoAFnvRn5G zOCuAoT1xRacxT0k*!2@yIW9Zvn=YPKl)woN)%=j_=wV`OSa+Ax$S6UTR{sY%-&5K3 zi`X;9)WAWXomx-BqgfnAqjyy^u|?DdwiEh%UWH#GIQr8X*1F^X_`@4x0rn~1gL<<; ziZ_=h5JarU9j?1FLM!>@TvJVQ1$ABdF{{bjqv17&)rak^BZtj5HwDu@R2t@Hbgp_( z^Ze>O0h8p9${J~Z_C~K$1xs4ff3Oh%bj+Uozb?CMPxzh>xbsbgsGbd~2Cg5)}JPDXz|k8$O6A`wa5{Epb1 zyUDpx_6N;fPozIj)X4dXt$~z@`!`po4YrX3%KCjYgO2OLTX-CisnEXu71o>*#1M-i zb$MFKkQWr@Zif#!p3YHX4kBDE4DuV11r@9JO1ElH+P@1f2dBJlKKa@O*%*Er_IWV& zBwl1D;8H@Z>MG(#d=SrWBU5Aa9U}z7M=~gif<{lMgof1Ej_nfV^w^bFh8O*pPkOxl zj2TlBn!dJ3vPCDH-(xW#*7Gh|^GyHM$lV8-WA^+zq=V8KL_oi4>aK~c%v>lM`4;Mn zk+SdL{~|p7YcMuh=Z!eq`81R1uULcGC307`?HVtJONW{aKHV2@aQsJRXqpGjyc z!WBeRW^Jq|l&G%Z9AHAbyH+zlAf~@-zrR^H=PKV=w7)BT2a@ZBJ05>g**+;gZxVFd z?|l$;m~xobj$o^KI!t3FsLEg?IZjh&-*6nF!&92`mF6|ca`WM7qJ*#8d@7No=+Vw| zv$pT}-KSS+j(268y5xDR8xQAYDlGUGLg#(E&fl^X*=z!{W`z4o9c~e_b*2Efg*mdkdj&IKNneze5A;t3a_no_R1u^$Zw~KcY$exGpue0>mk8-A7)D1eIHa}5 z?`!>u*zb+FO?oUv6iRG(_cfKalh_LQaTBoXi(pl(BnwAOHkBFJp0@;srq!Li6WNkj zu55q%HTe43MrK61R^meFMNQUzz00G1e^T0-9zq8P(Tjw{;}5_%Su}2pggxZAzAl^M zvY}UZ4iz8^%C^}{FQN`1cu*)fb*Ck%em4;cordiF=r~?lBhZd zUR1lC>^PLp_E8>qv!p7x?!${C4Ir;If{wS(`nl7mnT+d}l};LCRR!*ysj;YYZCDqa z4NWp6dHm`S_J;v`&^UVEo8g!2?;pA98fXH4In!_-ic45fSceJK3Q^)xYybnB-K(Na zuODi-cW>3QyfAw^C0x1R=O;B)>~yT+6&e^y(sXP);obhJ+2_DSe;1cod#`OnO+b~z zMslSh`8l^(-nQA59!`&snzyd8q?Tw6@2(G8&G$?q!ig-cCD%4ej2l+XibE@_^la5f zB6>aS?w)#FK(rmsS|%bEY)10ci}h9EfTcwuvzAJfp_ogWiX7%=#Bd+r_uBa4)StsM%5{{j^~w*q)oge6 zrTf57Z~bWxcSJgznEf6LxbkEM8L^d(%$6t+b?CkO1hQ@U8Z}T|jYvA#6LV!Xig#~a z5$>U_|3pzpm^y&1*p)T=75=Hd>7uB;p_j#o2i{b{ATcH0Xl#(t+1v~ok8^O{b3)b1u{{SdDSPF1)`>+1>9E9Ro1_wG#J+ky@ zgy3XnQJ&sk$gwoTXn2_dlzxBzkFr`?P5Dd_PR|M3{IVHax{k} zBkDb41Roe$K%d%&9!yVl#n?(( zBdoW2mvb@C1Ze8_U%c;qz1DD~!sB4bpKvVhc+hb78eFA@bMxZttk7xee71gGj>lW0 zxvVI~!(ze{nOZ32z3e_cR>y3wmzia&+Q95^+HwkM6;*k3kZcF>G-SH=j&n)XGJ%dB zSKCdO&lT0)gVlRnv`MqZ1 z<6^Dl3+~3sW`8|v{NfSoY-n#f#{Wm_QKKQMIQ zzG1bAhqob0Yj36?cXJ3`1|$b!i%z`O6G#IDSH^Y+AqmQnqy& z)tf67Dc6k4Z`)NO9)uYp<9$@2)|HqdY9+N>x4-*JK_<(Vnj|keHJ4T`)ll;XH?u)iX zr|F@?N}I_Ij`~k;9n+vk>bs;;Q(GN{Urh(nxwy0i5+ias2B*LQkWYz~9eb2jz_&6| z91?CQ-dC+Nwi}KuC(*Ns;2I8bPu(tRR}^1?L>-%VDRRFEq&GG2!WHu5! z7@<8E9I#gJ_8Nxvk4wkl?zVvj1G}BmG%s1DxacOz?t zWY(5*UW5+p+pbh(ER`=M*tSbrZImlqW2=cfPA=j#_))tz4{yH&rO@_)wwq~uo^SaV zZ1>crkmiPanPK{^ZW4YY?fZ{cYQMLVerg?&nm9JP<8m*m zw>_==m`F^b=QykWMVePj+8Pxql&&>Cxu@dAeLC}E8*y=BwYFRht!+en^$*%fLIro< zd2j_+J@y=<+6mpoztr^s{d-HuWB>B@fn#&yw!Hh0kOHZg9ENMy~`iMu7}5unAC31J2w4DK00B79M8B@HRDt{Y$TO%h$qk9 z*U$g-1U8xL9SG_n*F&_eHTkV`4RPPCub2F!8f(sitD~r|LlFI5oa(2#IsV-dlP6g& zOonym_VXi-R0a*cVPyMR8qf7Rx__e{Gm3hv3HsPq?*Y$7-O$)L0eCiLfh4=$K({{* z`qgbEhAlTPH+ZaWB3Y2@hx2C)FPusIio{nx*X)wISDkxUXJ$bz_xdl3?c;8WAMQDC zBPf@Hy$4?Mout$v7+Agvkdi6qu@2QE$AzNor>c7ySP-W>{LSIsMGd+45Bt)`M60Ht zwa5czBSyE2t*xjFvH1%IqsGda-M+1+a;MNfd(sIK9cu5Pj5YB+;ovG&F?|v3J^za{ z>2NZMN2Sh^KY}65xdBFQ`!k1JwT;0aJ<9+J8u5Arp7TvJ0tZD&u7)V0DbIQMl*o)P zmxu3|G^V}phvCxQTz7{?ziwo;Y(8IVX9e&aD-Tu~jj4I0ek@)xYWMz)WbA-sMKfnTbqs>)__

gK+Xk#m%s?d7waMCxQ)=#enqjR?`O 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%0AYP||lwpTDRn5PlomqHOPVl9>Ez#=%Y1?(e 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

jkJ^1rg5G!-Y0Qe4+(J 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

ufORAIF+nZ8v{;ni^ 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%0DAL;9nA&$RZ;tS;NS?Y?w8*Q{ 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 diff --git a/gateway/middlewares_test.go b/gateway/middlewares_test.go new file mode 100644 index 0000000..89723ea --- /dev/null +++ b/gateway/middlewares_test.go @@ -0,0 +1,65 @@ +package gateway + +import ( + "context" + "net/http" + "testing" + + hello "github.com/begonia-org/go-sdk/api/example/v1" + c "github.com/smartystreets/goconvey/convey" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +type responseWriter struct { + header http.Header +} + +func (r *responseWriter) Header() http.Header { + return r.header +} +func (r *responseWriter) Write([]byte) (int, error) { + return 0, nil +} +func (r *responseWriter) WriteHeader(int) { + +} +func TestLoggerMiddlewares(t *testing.T) { + mid :=NewLoggerMiddleware(Log) + c.Convey("TestLoggerMiddlewares panic", t, func() { + f := func() { + _, _ = mid.UnaryInterceptor(context.TODO(), nil, &grpc.UnaryServerInfo{ + FullMethod: "/test", + }, func(ctx context.Context, req any) (any, error) { + panic("test") + }) + } + c.So(f, c.ShouldNotPanic) + f2 := func() { + _ = mid.StreamInterceptor(nil, &streamMock{}, &grpc.StreamServerInfo{FullMethod: "/test"}, func(srv interface{}, ss grpc.ServerStream) error { + panic("test") + }) + } + c.So(f2, c.ShouldNotPanic) + }) +} + +func TestIncomingHeadersToMetadata(t *testing.T) { + c.Convey("TestIncomingHeadersToMetadata", t, func() { + req, _ := http.NewRequest(http.MethodGet, "http://localhost", nil) + req.Header.Add("test", "test") + req.Header.Add("pragma", ":") + req.Header.Add(XAccessKey, "123456") + md := IncomingHeadersToMetadata(context.TODO(), req) + c.So(md, c.ShouldNotBeNil) + }) +} +func TestHttpResponseBodyModify(t *testing.T) { + c.Convey("TestHttpResponseBodyModify", t, func() { + + resp := &responseWriter{header: make(http.Header)} + ctx:=metadata.NewIncomingContext(context.Background(),metadata.Pairs(XAccessKey,"123456")) + resp2 := HttpResponseBodyModify(ctx, resp, &hello.HelloReply{}) + c.So(resp2, c.ShouldBeNil) + }) +} diff --git a/gateway/websocket_test.go b/gateway/websocket_test.go new file mode 100644 index 0000000..d678e9b --- /dev/null +++ b/gateway/websocket_test.go @@ -0,0 +1,25 @@ +package gateway + +import ( + "fmt" + "testing" + + "github.com/agiledragon/gomonkey/v2" + "github.com/gorilla/websocket" + c "github.com/smartystreets/goconvey/convey" +) + +func TestWebsocketForwarder(t *testing.T) { + c.Convey("test websocket forwarder", t, func() { + wk := &websocketForwarder{ + websocket: &websocket.Conn{}, + } + patch:=gomonkey.ApplyFuncReturn((*websocket.Conn).WriteMessage, fmt.Errorf("write error")) + defer patch.Reset() + _,err := wk.Write([]byte("test")) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "write error") + patch.Reset() + + }) +} From ccb71477d6dad32c85859df4a003e75ce6785457 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Sun, 16 Jun 2024 00:45:29 +0800 Subject: [PATCH 06/16] chore:add config value --- .github/workflows/go.yml | 2 +- cmd/begonia/main.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4ec1fd2..d49a6ba 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -29,7 +29,7 @@ jobs: - name: Init MySQL run: | go mod tidy - go run ./cmd/begonia init -e dev + go run ./cmd/begonia init -c ./config/settings.yml -e dev cat ~/.begonia/admin-app.json - name: Init Minio run: | diff --git a/cmd/begonia/main.go b/cmd/begonia/main.go index d136ed4..2305c28 100644 --- a/cmd/begonia/main.go +++ b/cmd/begonia/main.go @@ -19,7 +19,6 @@ import ( func addCommonCommand(cmd *cobra.Command) *cobra.Command { cmd.Flags().StringP("env", "e", "dev", "Runtime Environment") cmd.Flags().StringP("config", "c", "./config/settings.yml", "Config file path") - _ = cmd.MarkFlagRequired("config") return cmd } func NewInitCmd() *cobra.Command { From 88609f28d0788a5c156a03bd50d5f3f1837b43e0 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Sun, 16 Jun 2024 00:59:30 +0800 Subject: [PATCH 07/16] chore:add config value --- codecov.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codecov.yml b/codecov.yml index d5e44ba..a7e740c 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,4 +1,5 @@ ignore: - "cmd" - "version.go" - - "example" \ No newline at end of file + - "example" + - "config" \ No newline at end of file From 3e34787531494dc3a8b01212061f2d67b8e7b867 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 17 Jun 2024 01:54:07 +0800 Subject: [PATCH 08/16] feat:get file by id --- gateway/endpoint.go | 1 - gateway/exception.go | 2 +- gateway/protobuf.go | 2 +- go.mod | 4 +- go.sum | 8 +-- internal/biz/file/file.go | 38 ++++++++++-- internal/biz/file/file_test.go | 15 +++++ internal/biz/file/minio.go | 19 +++++- internal/biz/file/minio_test.go | 11 ++++ internal/data/curd.go | 1 + internal/data/file.go | 19 +++++- internal/data/file_test.go | 33 +++++++++- internal/middleware/vaildator.go | 20 ++++++ internal/middleware/vaildator_test.go | 25 ++++++++ internal/server/coverprofile.cov | 32 +++++----- internal/server/server.go | 2 + internal/service/file.go | 88 ++++++++++++++++++++------- internal/service/file_test.go | 23 ++++++- internal/worker.go | 1 + 19 files changed, 283 insertions(+), 61 deletions(-) diff --git a/gateway/endpoint.go b/gateway/endpoint.go index 0e61ad4..a91e85a 100644 --- a/gateway/endpoint.go +++ b/gateway/endpoint.go @@ -50,7 +50,6 @@ func (e *httpForwardGrpcEndpointImpl) Request(req GrpcRequest) (proto.Message, r out := req.GetOut() in := req.GetIn() ctx := req.GetContext() - err = conn.Invoke(ctx, req.GetFullMethodName(), in, out, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) return out, metadata, err diff --git a/gateway/exception.go b/gateway/exception.go index 030d461..7f3da65 100644 --- a/gateway/exception.go +++ b/gateway/exception.go @@ -37,7 +37,7 @@ func (e *Exception) UnaryInterceptor(ctx context.Context, req interface{}, info return nil, err } func (e *Exception) handlePanic(p interface{}) error { - const maxFrames = 5 + const maxFrames = 10 var pcs [maxFrames]uintptr n := runtime.Callers(2, pcs[:]) // skip first 3 frames diff --git a/gateway/protobuf.go b/gateway/protobuf.go index 1f8db3e..d1b80b9 100644 --- a/gateway/protobuf.go +++ b/gateway/protobuf.go @@ -66,7 +66,6 @@ func (p *protobufDescription) SetHttpResponse(option protoreflect.ExtensionType) return fmt.Errorf("invalid gateway.json") } svr := svrAndMethod[0] - desc, err := p.fs.FindDescriptorByName(protoreflect.FullName(svr)) if err != nil { return err @@ -174,6 +173,7 @@ func NewDescriptionFromBinary(data []byte, outDir string) (ProtobufDescription, return nil, fmt.Errorf("Failed to write gateway.json: %w", err) } desc.gatewayJsonSchema = filepath.Join(outDir, "gateway.json") + // log.Printf("gateway.json:%s", desc.gatewayJsonSchema) return desc, nil } func (p *protobufDescription) GetFileDescriptorSet() *descriptorpb.FileDescriptorSet { diff --git a/go.mod b/go.mod index 6a1eccf..45bb70d 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cockroachdb/errors v1.11.1 github.com/google/wire v0.6.0 github.com/smartystreets/goconvey v1.8.1 - github.com/spark-lence/tiga v0.0.0-20240615083703-1e293faf6e11 + github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643 github.com/spf13/cobra v1.8.0 google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 google.golang.org/grpc v1.64.0 @@ -80,7 +80,7 @@ require ( require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240615083549-ad7c60806f6a + github.com/begonia-org/go-sdk v0.0.0-20240616112226-0e6ed4e2a9b2 github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 diff --git a/go.sum b/go.sum index 306dc4e..c31f075 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa h1:DH github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa/go.mod h1:xEqoca1vNGqH8CV7X9EzhDV5Ihtq9J95p7ZipzUB6pc= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw7T+90KC5QW7yCa8Nn/5psnlvsexipAOrQAcc7YE0= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= -github.com/begonia-org/go-sdk v0.0.0-20240615083549-ad7c60806f6a h1:wOekXxD7AfUyptf49fMJqK2u+su6Wo7uCl5NxWM5bho= -github.com/begonia-org/go-sdk v0.0.0-20240615083549-ad7c60806f6a/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240616112226-0e6ed4e2a9b2 h1:Bxx3CAQnYw6cDTY9h4lqfdYCMMZ6n10QsRXsLKAKlxM= +github.com/begonia-org/go-sdk v0.0.0-20240616112226-0e6ed4e2a9b2/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -229,8 +229,8 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spark-lence/tiga v0.0.0-20240615083703-1e293faf6e11 h1:D7LhqyeoUVS23NZmziKBNIW9jc3xvrBRg4fcwZPR4uM= -github.com/spark-lence/tiga v0.0.0-20240615083703-1e293faf6e11/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= +github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643 h1:hFu6LvrCrPBpPU8JbgsxKPSQK+M6A5O2TzO5o7w9c70= +github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= diff --git a/internal/biz/file/file.go b/internal/biz/file/file.go index a7cc37d..e68e388 100644 --- a/internal/biz/file/file.go +++ b/internal/biz/file/file.go @@ -6,6 +6,7 @@ import ( goErr "errors" "fmt" "io" + "log" "net/http" "os" "path/filepath" @@ -34,7 +35,9 @@ type FileRepo interface { DelFile(ctx context.Context, engine, bucket, key string) error UpsertBucket(ctx context.Context, bucket *api.Buckets) error DelBucket(ctx context.Context, bucketId string) error + GetFileById(ctx context.Context, fid string) (*api.Files, error) List(ctx context.Context, page, pageSize int32, bucket, engine, owner string) ([]*api.Files, error) + GetFile(ctx context.Context, engine, bucket, key string) (*api.Files, error) } type FileUsecase interface { Upload(ctx context.Context, in *api.UploadFileRequest, authorId string) (*api.UploadFileResponse, error) @@ -49,6 +52,7 @@ type FileUsecase interface { Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) + GetFileByID(ctx context.Context, fileId string) (*api.Files, error) } type FileUsecaseImpl struct { repo FileRepo @@ -285,7 +289,7 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, } } - err = f.repo.UpsertFile(ctx, &api.Files{ + fileObj:=&api.Files{ Uid: f.snowflake.GenerateIDString(), Engine: api.FileEngine_name[int32(api.FileEngine_FILE_ENGINE_LOCAL)], Bucket: in.Bucket, @@ -294,13 +298,15 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, Owner: authorId, CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), - }) + } + err = f.repo.UpsertFile(ctx, fileObj) if err != nil { return nil, err } return &api.UploadFileResponse{ Uri: uri, Version: commitId, + Uid: fileObj.Uid, }, err } @@ -437,6 +443,7 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a if authorId == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } + originKey := in.Key key, err := f.checkIn(in.Key) if err != nil { return nil, err @@ -488,17 +495,22 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") } } - - err = f.repo.UpsertFile(ctx, &api.Files{ + fileKey:=uri + if in.Engine!=api.FileEngine_FILE_ENGINE_LOCAL.String(){ + fileKey = originKey + } + fileObj:=&api.Files{ Uid: f.snowflake.GenerateIDString(), Engine: in.Engine, Bucket: in.Bucket, - Key: uri, + Key: fileKey, IsDeleted: false, Owner: authorId, CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), - }) + } + log.Printf("insert %s,%s,%s,%s",fileObj.Uid,fileObj.Bucket,fileObj.Key,fileObj.Engine) + err = f.repo.UpsertFile(ctx, fileObj) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") } @@ -507,6 +519,7 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a return &api.CompleteMultipartUploadResponse{ Uri: uri, Version: commit, + Uid: fileObj.Uid, }, err } @@ -584,6 +597,15 @@ func (f *FileUsecaseImpl) Metadata(ctx context.Context, in *api.FileMetadataRequ version, _ = f.Version(ctx, in.Bucket, originKey, authorId) } + uid:=in.FileId + if uid==""{ + fileObj,err:=f.repo.GetFile(ctx,in.Engine,in.Bucket,in.Key) + if err != nil { + return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") + } + uid=fileObj.Uid + } + return &api.FileMetadataResponse{ Size: file.Size(), ModifyTime: file.ModifyTime(), @@ -592,6 +614,7 @@ func (f *FileUsecaseImpl) Metadata(ctx context.Context, in *api.FileMetadataRequ Name: filepath.Base(in.Key), Etag: fmt.Sprintf("%d-%s", file.Size(), sha256), Version: version, + Uid: uid, }, nil } @@ -642,6 +665,9 @@ func (f *FileUsecaseImpl) checkStatusCode(err error) (int32, codes.Code) { return int32(common.Code_INTERNAL_ERROR), codes.Internal } } +func (f *FileUsecaseImpl)GetFileByID(ctx context.Context, fileId string) (*api.Files, error) { + return f.repo.GetFileById(ctx, fileId) +} func (f *FileUsecaseImpl) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) { key, err := f.checkIn(in.Key) diff --git a/internal/biz/file/file_test.go b/internal/biz/file/file_test.go index dbc3605..8f25518 100644 --- a/internal/biz/file/file_test.go +++ b/internal/biz/file/file_test.go @@ -771,6 +771,7 @@ func testCompleteMultipartUploadFile(t *testing.T) { UseVersion: true, Sha256: bigFileSha256, Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) @@ -898,6 +899,7 @@ func testFileMeta(t *testing.T) { Key: fileAuthor + "/test/upload.parts.test1", Version: "", Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(meta, c.ShouldNotBeNil) @@ -908,6 +910,7 @@ func testFileMeta(t *testing.T) { Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(meta, c.ShouldNotBeNil) @@ -961,6 +964,18 @@ func testFileMeta(t *testing.T) { Version: "latest", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "io.copy error") + patch3.Reset() + + patch4:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) + defer patch4.Reset() + _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ + Key: fileAuthor + "/test/upload.parts.test1", + Version: "latest", + Bucket: bucket, + }, fileAuthor) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get error") + patch4.Reset() }) } diff --git a/internal/biz/file/minio.go b/internal/biz/file/minio.go index 2675701..d9c5fdb 100644 --- a/internal/biz/file/minio.go +++ b/internal/biz/file/minio.go @@ -59,7 +59,7 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au return nil, gosdk.NewError(fmt.Errorf("failed to upload object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upload_object") } - err = m.localFile.repo.UpsertFile(ctx, &api.Files{ + f:=&api.Files{ Uid: m.localFile.snowflake.GenerateIDString(), Engine: api.FileEngine_name[int32(*api.FileEngine_FILE_ENGINE_MINIO.Enum())], Bucket: in.Bucket, @@ -68,13 +68,14 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au Owner: authorId, CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), - }) + } + err = m.localFile.repo.UpsertFile(ctx, f) if err != nil { return nil, gosdk.NewError(fmt.Errorf("failed to upsert file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") } // log.Printf("upload object:%s,%s", info.Key, info.ChecksumSHA256) - return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID}, err + return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID,Uid: f.Uid}, err } func (m *MinioUseCase) InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { return m.localFile.InitiateUploadFile(ctx, in) @@ -162,6 +163,14 @@ func (m *MinioUseCase) Metadata(ctx context.Context, in *api.FileMetadataRequest if checksum == "" { checksum = info.UserMetadata["Sha256"] } + fileId:=in.FileId + if fileId==""{ + file,err:=m.localFile.repo.GetFile(ctx,in.Engine,in.Bucket,in.Key) + if err!=nil{ + return nil, gosdk.NewError(fmt.Errorf("failed to get file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") + } + fileId=file.Uid + } return &api.FileMetadataResponse{Size: info.Size, ContentType: info.ContentType, Version: info.VersionID, @@ -169,6 +178,7 @@ func (m *MinioUseCase) Metadata(ctx context.Context, in *api.FileMetadataRequest Etag: info.ETag, Name: info.Key, Sha256: checksum, + Uid: fileId, }, nil } func (m *MinioUseCase) Version(ctx context.Context, bucket, key, authorId string) (string, error) { @@ -206,3 +216,6 @@ func (m *MinioUseCase) Delete(ctx context.Context, in *api.DeleteRequest, author func (m *MinioUseCase) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) { return m.localFile.List(ctx, in, authorId) } +func (m *MinioUseCase) GetFileByID(ctx context.Context, fileId string) (*api.Files, error){ + return m.localFile.GetFileByID(ctx, fileId) +} \ No newline at end of file diff --git a/internal/biz/file/minio_test.go b/internal/biz/file/minio_test.go index ac48db0..21f36a6 100644 --- a/internal/biz/file/minio_test.go +++ b/internal/biz/file/minio_test.go @@ -180,6 +180,16 @@ func testMinioMeta(t *testing.T) { patch.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "get meta error") + + patch4:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) + defer patch4.Reset() + _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ + Key: "test.txt", + Bucket: minioBucket, + }, fileAuthor) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get error") + patch4.Reset() }) } @@ -292,6 +302,7 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { ContentType: "text/plain", UseVersion: true, Sha256: minioBigFileSha256, + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) diff --git a/internal/data/curd.go b/internal/data/curd.go index e2179f1..9ee59d6 100644 --- a/internal/data/curd.go +++ b/internal/data/curd.go @@ -73,6 +73,7 @@ func (c *curdImpl) Get(ctx context.Context, model interface{}, needDecrypt bool, query = fmt.Sprintf("(%s) and is_deleted=0", query) } + // log.Printf("query:%s,args:%v", query,args) if err := c.db.First(ctx, model, query, args...); err != nil { return fmt.Errorf("get model failed: %w", err) } diff --git a/internal/data/file.go b/internal/data/file.go index 4f2d5d4..f26f184 100644 --- a/internal/data/file.go +++ b/internal/data/file.go @@ -27,6 +27,7 @@ func (f *fileRepoImpl) UpsertFile(ctx context.Context, in *api.Files) error { if in.UpdateMask != nil { mask = in.UpdateMask.Paths } + // log.Printf("mask:%v", in.Uid) return f.data.db.Upsert(ctx, in, mask...) } func (f *fileRepoImpl) DelFile(ctx context.Context, engine, bucket, key string) error { @@ -44,6 +45,22 @@ func (f *fileRepoImpl) UpsertBucket(ctx context.Context, bucket *api.Buckets) er func (f *fileRepoImpl) DelBucket(ctx context.Context, bucketId string) error { return f.curd.Del(ctx, &api.Buckets{Uid: bucketId}, false) } +func (f *fileRepoImpl) GetFileById(ctx context.Context, fid string) (*api.Files, error) { + file := &api.Files{Uid: fid} + err := f.curd.Get(ctx, file, false, "uid = ?", fid) + if err != nil { + return nil, err + } + return file, nil +} +func (f *fileRepoImpl) GetFile(ctx context.Context, engine, bucket, key string) (*api.Files, error) { + file := &api.Files{} + err := f.curd.Get(ctx, file, false, "`file_key`=? and `bucket`=? and `fs_engine`=?", key, bucket, engine) + if err != nil { + return nil, err + } + return file, nil +} func (f *fileRepoImpl) List(ctx context.Context, page, pageSize int32, bucket, engine, owner string) ([]*api.Files, error) { files := make([]*api.Files, 0) pagination := &tiga.Pagination{ @@ -58,7 +75,7 @@ func (f *fileRepoImpl) List(ctx context.Context, page, pageSize int32, bucket, e pagination.Args = append(pagination.Args, bucket) } if engine != "" { - pagination.Query = fmt.Sprintf("%s and engine = ?", pagination.Query) + pagination.Query = fmt.Sprintf("%s and fs_engine = ?", pagination.Query) pagination.Args = append(pagination.Args, engine) } err := f.curd.List(ctx, &files, pagination) diff --git a/internal/data/file_test.go b/internal/data/file_test.go index fe23d10..5516ba4 100644 --- a/internal/data/file_test.go +++ b/internal/data/file_test.go @@ -17,7 +17,7 @@ import ( ) var fileBucketId = "" - +var fileFileId="" func testUpsertFile(t *testing.T) { c.Convey("Test UpsertFile", t, func() { env := "dev" @@ -27,16 +27,18 @@ func testUpsertFile(t *testing.T) { conf := cfg.ReadConfig(env) f := NewFileRepo(conf, gateway.Log) snk, _ := tiga.NewSnowflake(1) + fileFileId = snk.GenerateIDString() file := &api.Files{ Engine: "test", Bucket: "test", Key: "test", - Uid: snk.GenerateIDString(), + Uid: fileFileId, Owner: "test", CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), UpdateMask: &fieldmaskpb.FieldMask{}, } + // t.Logf("fileFileId:%s,file id %s",fileFileId,file.Uid) err := f.UpsertFile(context.Background(), file) c.So(err, c.ShouldBeNil) @@ -46,7 +48,33 @@ func testUpsertFile(t *testing.T) { c.So(err, c.ShouldBeNil) }) } +func testGetFileById(t *testing.T) { + c.Convey("Test GetFileById", t, func(){ + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + file,err := f.GetFileById(context.Background(),fileFileId) + c.So(err,c.ShouldBeNil) + c.So(file,c.ShouldNotBeNil) + + file,err=f.GetFile(context.Background(),"test","test","test") + c.So(err,c.ShouldBeNil) + c.So(file,c.ShouldNotBeNil) + patch:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) + defer patch.Reset() + _,err = f.GetFileById(context.Background(),"") + c.So(err,c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,"get error") + _,err=f.GetFile(context.Background(),"test","test","test") + c.So(err,c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,"get error") + + }) +} func testUpsertBucket(t *testing.T) { c.Convey("Test UpsertBucket", t, func() { env := "dev" @@ -127,6 +155,7 @@ func testDeleteBucket(t *testing.T) { } func TestFile(t *testing.T) { t.Run("TestUpsertFile", testUpsertFile) + t.Run("TestGetFileById", testGetFileById) t.Run("TestUpsertBucket", testUpsertBucket) t.Run("TestFileList", testFileList) t.Run("TestDeleteFile", testDeleteFile) diff --git a/internal/middleware/vaildator.go b/internal/middleware/vaildator.go index c23ccb1..0b78362 100644 --- a/internal/middleware/vaildator.go +++ b/internal/middleware/vaildator.go @@ -130,8 +130,28 @@ func (p *ParamsValidatorImpl) FiltersFields(v interface{}, parent string) []stri } return nil } +func RegisterCustomValidators(v *validator.Validate) { + _=v.RegisterValidation("required_if", requiredIf) +} + +// requiredIf 自定义验证器逻辑 +func requiredIf(fl validator.FieldLevel) bool { + param := fl.Param() + field := fl.Field() + + // 获取参数字段值 + paramField := fl.Parent().FieldByName(param) + + // 如果参数字段为空,当前字段必须非空 + if paramField.String() == "" { + return field.String() != "" + } + + return true +} func (p *ParamsValidatorImpl) ValidateParams(v interface{}) error { validate := validator.New() + RegisterCustomValidators(validate) err := validate.Struct(v) filters := p.FiltersFields(v, "") if len(filters) > 0 { diff --git a/internal/middleware/vaildator_test.go b/internal/middleware/vaildator_test.go index 187eeb7..7279f76 100644 --- a/internal/middleware/vaildator_test.go +++ b/internal/middleware/vaildator_test.go @@ -83,7 +83,32 @@ func TestValidatorUnaryInterceptor(t *testing.T) { c.So(err, c.ShouldBeNil) }) } +func TestRequireIf(t *testing.T) { + c.Convey("test require if", t, func() { + v := []struct { + Field string `validate:"required_if=Field2"` + Field2 string + Field3 string `validate:"required_if=Field2"` + }{{ + Field: "", + Field2: "test", + Field3: "", + }, + { + Field: "", + Field2: "", + Field3: "", + }, + } + validator := middleware.NewParamsValidator() + err:=validator.ValidateParams(v[0]) + c.So(err, c.ShouldBeNil) + err=validator.ValidateParams(v[1]) + c.So(err,c.ShouldNotBeNil) + }) + +} func TestValidatorStreamInterceptor(t *testing.T) { c.Convey("test stream interceptor", t, func() { validator := middleware.NewParamsValidator() diff --git a/internal/server/coverprofile.cov b/internal/server/coverprofile.cov index 43ef396..a826e5e 100644 --- a/internal/server/coverprofile.cov +++ b/internal/server/coverprofile.cov @@ -1,17 +1,17 @@ mode: set -github.com/begonia-org/begonia/internal/server/server.go:26.57,33.2 3 1 -github.com/begonia-org/begonia/internal/server/server.go:34.73,37.16 3 1 -github.com/begonia-org/begonia/internal/server/server.go:37.16,39.3 1 1 -github.com/begonia-org/begonia/internal/server/server.go:40.2,41.16 2 1 -github.com/begonia-org/begonia/internal/server/server.go:41.16,43.3 1 1 -github.com/begonia-org/begonia/internal/server/server.go:44.2,45.16 2 1 -github.com/begonia-org/begonia/internal/server/server.go:45.16,47.3 1 1 -github.com/begonia-org/begonia/internal/server/server.go:48.2,48.16 1 1 -github.com/begonia-org/begonia/internal/server/server.go:50.155,76.16 15 1 -github.com/begonia-org/begonia/internal/server/server.go:76.16,77.13 1 1 -github.com/begonia-org/begonia/internal/server/server.go:79.2,87.31 5 1 -github.com/begonia-org/begonia/internal/server/server.go:87.31,89.17 2 1 -github.com/begonia-org/begonia/internal/server/server.go:89.17,90.14 1 1 -github.com/begonia-org/begonia/internal/server/server.go:92.3,92.45 1 1 -github.com/begonia-org/begonia/internal/server/server.go:92.45,94.4 1 1 -github.com/begonia-org/begonia/internal/server/server.go:97.2,99.11 2 1 +github.com/begonia-org/begonia/internal/server/server.go:27.57,34.2 3 1 +github.com/begonia-org/begonia/internal/server/server.go:35.73,39.16 4 1 +github.com/begonia-org/begonia/internal/server/server.go:39.16,41.3 1 1 +github.com/begonia-org/begonia/internal/server/server.go:42.2,43.16 2 1 +github.com/begonia-org/begonia/internal/server/server.go:43.16,45.3 1 1 +github.com/begonia-org/begonia/internal/server/server.go:46.2,47.16 2 1 +github.com/begonia-org/begonia/internal/server/server.go:47.16,49.3 1 1 +github.com/begonia-org/begonia/internal/server/server.go:50.2,50.16 1 1 +github.com/begonia-org/begonia/internal/server/server.go:52.155,78.16 15 1 +github.com/begonia-org/begonia/internal/server/server.go:78.16,79.13 1 1 +github.com/begonia-org/begonia/internal/server/server.go:81.2,88.31 5 1 +github.com/begonia-org/begonia/internal/server/server.go:88.31,90.17 2 1 +github.com/begonia-org/begonia/internal/server/server.go:90.17,91.14 1 1 +github.com/begonia-org/begonia/internal/server/server.go:93.3,93.45 1 1 +github.com/begonia-org/begonia/internal/server/server.go:93.45,95.4 1 1 +github.com/begonia-org/begonia/internal/server/server.go:98.2,100.11 2 1 diff --git a/internal/server/server.go b/internal/server/server.go index 2e36021..4dc9558 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -3,6 +3,7 @@ package server import ( "context" "fmt" + "log" "net" "net/http" "os" @@ -33,6 +34,7 @@ func NewGatewayConfig(gw string) *gateway.GatewayConfig { } func readDesc(conf *config.Config) (gateway.ProtobufDescription, error) { desc := conf.GetLocalAPIDesc() + log.Printf("read desc file:%s", desc) bin, err := os.ReadFile(desc) if err != nil { return nil, fmt.Errorf("read desc file error:%w", err) diff --git a/internal/service/file.go b/internal/service/file.go index 15ce061..1041375 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "log" "net/http" "net/url" "strconv" @@ -22,6 +23,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/reflect/protoreflect" ) type FileService struct { @@ -29,6 +31,14 @@ type FileService struct { biz map[string]file.FileUsecase config *config.Config } +type ReadFileRequest interface { + GetEngine() string + GetBucket() string + GetKey() string + GetVersion() string + ProtoReflect() protoreflect.Message + GetFileId() string +} func NewFileService(biz map[string]file.FileUsecase, config *config.Config) api.FileServiceServer { return &FileService{biz: biz, config: config} @@ -44,11 +54,21 @@ func (f *FileService) checkFsEngine(engine string) error { } return nil } -func (f *FileService) serviceDecorator(_ context.Context, req request, handle func() (interface{}, error)) (interface{}, error) { +func (f *FileService) serviceDecorator(ctx context.Context, req request, handle func(in request) (interface{}, error)) (interface{}, error) { if err := f.checkFsEngine(req.GetEngine()); err != nil { return nil, err } - return handle() + if in, ok := req.(ReadFileRequest); ok { + if in.GetFileId() != "" { + var err error + req, err = f.getFile(ctx, in) + if err != nil { + return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "get_file_by_id") + } + } + } + + return handle(req) } func (f *FileService) Upload(ctx context.Context, in *api.UploadFileRequest) (*api.UploadFileResponse, error) { @@ -57,8 +77,8 @@ func (f *FileService) Upload(ctx context.Context, in *api.UploadFileRequest) (*a return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.GetEngine()].Upload(ctx, in, identity) + rsp, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.GetEngine()].Upload(ctx, req.(*api.UploadFileRequest), identity) }) r, _ := rsp.(*api.UploadFileResponse) return r, err @@ -66,15 +86,15 @@ func (f *FileService) Upload(ctx context.Context, in *api.UploadFileRequest) (*a } func (f *FileService) InitiateMultipartUpload(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { - rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].InitiateUploadFile(ctx, in) + rsp, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].InitiateUploadFile(ctx, req.(*api.InitiateMultipartUploadRequest)) }) r, _ := rsp.(*api.InitiateMultipartUploadResponse) return r, err } func (f *FileService) UploadMultipartFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) { - rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].UploadMultipartFileFile(ctx, in) + rsp, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].UploadMultipartFileFile(ctx, req.(*api.UploadMultipartFileRequest)) }) r, _ := rsp.(*api.UploadMultipartFileResponse) return r, err @@ -84,33 +104,37 @@ func (f *FileService) CompleteMultipartUpload(ctx context.Context, in *api.Compl if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].CompleteMultipartUploadFile(ctx, in, identity) + rsp, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].CompleteMultipartUploadFile(ctx, req.(*api.CompleteMultipartUploadRequest), identity) }) r, _ := rsp.(*api.CompleteMultipartUploadResponse) return r, err } func (f *FileService) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { - rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].AbortMultipartUpload(ctx, in) + rsp, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].AbortMultipartUpload(ctx, req.(*api.AbortMultipartUploadRequest)) }) r, _ := rsp.(*api.AbortMultipartUploadResponse) return r, err } +func (f *FileService) GetFileById(ctx context.Context, fid, engine string) (*api.Files, error) { + + return f.biz[engine].GetFileByID(ctx, fid) +} func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*httpbody.HttpBody, error) { identity := "" if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - newKey, err := url.PathUnescape(in.Key) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "url_unescape") } + log.Printf("Download file id:%s", in.GetFileId()) in.Key = newKey - r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].Download(ctx, in, identity) + r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].Download(ctx, req.(*api.DownloadRequest), identity) }) if err != nil { return nil, err @@ -120,9 +144,15 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h shaer := sha256.New() shaer.Write(buf) + meta, err := f.Metadata(ctx, &api.FileMetadataRequest{Bucket: in.Bucket, Engine: in.Engine, Key: in.Key, Version: in.Version}) + if err != nil { + return nil, err + } rspMd := metadata.Pairs( gosdk.GetMetadataKey("Content-Length"), fmt.Sprintf("%d", len(buf)), gosdk.GetMetadataKey("X-File-Sha256"), hex.EncodeToString(shaer.Sum(nil)), + gosdk.GetMetadataKey("Content-Type"), meta.ContentType, + gosdk.GetMetadataKey("X-File-Version"), meta.Version, ) _ = grpc.SendHeader(ctx, rspMd) @@ -226,19 +256,31 @@ func (f *FileService) Delete(ctx context.Context, in *api.DeleteRequest) (*api.D if identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].Delete(ctx, in, identity) + r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].Delete(ctx, req.(*api.DeleteRequest), identity) }) rsp, _ := r.(*api.DeleteResponse) return rsp, err } +func (f *FileService) getFile(ctx context.Context, in ReadFileRequest) (ReadFileRequest, error) { + file, err := f.GetFileById(ctx, in.GetFileId(), in.GetEngine()) + if err != nil { + return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "get_file_by_id") + } + fields := in.ProtoReflect().Descriptor().Fields() + in.ProtoReflect().Set(fields.ByName("bucket"), protoreflect.ValueOfString(file.Bucket)) + in.ProtoReflect().Set(fields.ByName("key"), protoreflect.ValueOfString(file.Key)) + in.ProtoReflect().Set(fields.ByName("engine"), protoreflect.ValueOfString(file.Engine)) + return in, nil +} func (f *FileService) Metadata(ctx context.Context, in *api.FileMetadataRequest) (*api.FileMetadataResponse, error) { identity := GetIdentity(ctx) if identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].Metadata(ctx, in, identity) + // log.Printf("get file metadata:%s,%s,%s", in.Bucket, in.Key, in.Engine) + r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].Metadata(ctx, req.(*api.FileMetadataRequest), identity) }) if err != nil { return nil, err @@ -280,8 +322,8 @@ func (f *FileService) ListFiles(ctx context.Context, in *api.ListFilesRequest) ( if identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].List(ctx, in, identity) + r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].List(ctx, req.(*api.ListFilesRequest), identity) }) if err != nil { return nil, err @@ -290,8 +332,8 @@ func (f *FileService) ListFiles(ctx context.Context, in *api.ListFilesRequest) ( return &api.ListFilesResponse{Files: files}, err } func (f *FileService) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { - r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { - return f.biz[in.Engine].MakeBucket(ctx, in) + r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { + return f.biz[in.Engine].MakeBucket(ctx, req.(*api.MakeBucketRequest)) }) rsp, _ := r.(*api.MakeBucketResponse) return rsp, err diff --git a/internal/service/file_test.go b/internal/service/file_test.go index b7afa02..7a45b26 100644 --- a/internal/service/file_test.go +++ b/internal/service/file_test.go @@ -34,7 +34,7 @@ import ( ) var fileBucket = "" - +var localFileId="" func sumFileSha256(src string) (string, error) { file, err := os.Open(src) if err != nil { @@ -89,6 +89,7 @@ func upload(t *testing.T) { c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) c.So(rsp.Uri, c.ShouldNotBeEmpty) + localFileId = rsp.GetUid() conf := cfg.NewConfig(config.ReadConfig(env)) filePath := filepath.Join(conf.GetUploadDir(), fileBucket, rsp.Uri) @@ -213,6 +214,26 @@ func download(t *testing.T) { c.So(err, c.ShouldBeNil) t.Log(sha256Str) c.So(sha256Str, c.ShouldEqual, downloadedSha256) + + + patch2 := gomonkey.ApplyFuncReturn((*service.FileService).Metadata, nil, fmt.Errorf("test metadata error")) + defer patch2.Reset() + _, err = apiClient.DownloadFile(context.Background(), sdkAPPID+"/test/helloworld.pb", tmp.Name(), "", fileBucket) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "unknown error") + patch2.Reset() + + // query by fid + sha256Str, err = apiClient.DownloadFile(context.Background(), localFileId, tmp.Name(), "", fileBucket) + c.So(err,c.ShouldBeNil) + c.So(sha256Str,c.ShouldNotBeEmpty) + + patch3:=gomonkey.ApplyFuncReturn((*service.FileService).GetFileById, nil, fmt.Errorf("test get file by id error")) + defer patch3.Reset() + _, err = apiClient.DownloadFile(context.Background(), localFileId, tmp.Name(), "", fileBucket) + patch3.Reset() + c.So(err,c.ShouldNotBeNil) + }) } func downloadParts(t *testing.T) { diff --git a/internal/worker.go b/internal/worker.go index e42ec52..3b412c3 100644 --- a/internal/worker.go +++ b/internal/worker.go @@ -32,4 +32,5 @@ func (g *GatewayWorkerImpl) Start() { g.daemon.Start(context.Background()) time.Sleep(time.Second * 2) g.server.Start() + } From cf57011811917a80e164a99a40d7347a1592d37e Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 17 Jun 2024 09:38:51 +0800 Subject: [PATCH 09/16] feat:update go-sdk version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 45bb70d..81da997 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240616112226-0e6ed4e2a9b2 + github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12 github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 diff --git a/go.sum b/go.sum index c31f075..f7fcafa 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa h1:DH github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa/go.mod h1:xEqoca1vNGqH8CV7X9EzhDV5Ihtq9J95p7ZipzUB6pc= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw7T+90KC5QW7yCa8Nn/5psnlvsexipAOrQAcc7YE0= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= -github.com/begonia-org/go-sdk v0.0.0-20240616112226-0e6ed4e2a9b2 h1:Bxx3CAQnYw6cDTY9h4lqfdYCMMZ6n10QsRXsLKAKlxM= -github.com/begonia-org/go-sdk v0.0.0-20240616112226-0e6ed4e2a9b2/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12 h1:4mZUWCrcl9gIoZaTUeQ9Qrty09WrK+eu2U5aipCy0Cw= +github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= From 3a126ee352071acd440508fbe9a1bab8d70d4aac Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 17 Jun 2024 10:19:21 +0800 Subject: [PATCH 10/16] feat:add file bucket --- go.mod | 4 ++-- go.sum | 9 +++++++++ internal/biz/file/file.go | 33 ++++++++++++++++++--------------- internal/biz/file/minio.go | 25 +++++++++++++++---------- internal/service/file.go | 30 +++++++++++++++--------------- 5 files changed, 59 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 81da997..453bcd4 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cockroachdb/errors v1.11.1 github.com/google/wire v0.6.0 github.com/smartystreets/goconvey v1.8.1 - github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643 + github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4 github.com/spf13/cobra v1.8.0 google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 google.golang.org/grpc v1.64.0 @@ -80,7 +80,7 @@ require ( require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12 + github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38 github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 diff --git a/go.sum b/go.sum index f7fcafa..9a486da 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoa github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -24,6 +25,8 @@ github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12 h1:4mZUWCrcl9gIoZaTUeQ9Qrty09WrK+eu2U5aipCy0Cw= github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38 h1:jY6dVk3lzs8gHQBqLfqdZvz8L5u/MwGx9baTJK8Fj7Y= +github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -118,6 +121,7 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -161,6 +165,7 @@ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -202,6 +207,7 @@ github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -231,6 +237,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643 h1:hFu6LvrCrPBpPU8JbgsxKPSQK+M6A5O2TzO5o7w9c70= github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= +github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4 h1:HmuGbn+mhKpi5tM8iM5vpUUx4K+XZZdrPecNu8IeO8c= +github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= @@ -406,6 +414,7 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/internal/biz/file/file.go b/internal/biz/file/file.go index e68e388..7aae196 100644 --- a/internal/biz/file/file.go +++ b/internal/biz/file/file.go @@ -35,7 +35,7 @@ type FileRepo interface { DelFile(ctx context.Context, engine, bucket, key string) error UpsertBucket(ctx context.Context, bucket *api.Buckets) error DelBucket(ctx context.Context, bucketId string) error - GetFileById(ctx context.Context, fid string) (*api.Files, error) + GetFileById(ctx context.Context, fid string) (*api.Files, error) List(ctx context.Context, page, pageSize int32, bucket, engine, owner string) ([]*api.Files, error) GetFile(ctx context.Context, engine, bucket, key string) (*api.Files, error) } @@ -289,7 +289,7 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, } } - fileObj:=&api.Files{ + fileObj := &api.Files{ Uid: f.snowflake.GenerateIDString(), Engine: api.FileEngine_name[int32(api.FileEngine_FILE_ENGINE_LOCAL)], Bucket: in.Bucket, @@ -306,7 +306,7 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, return &api.UploadFileResponse{ Uri: uri, Version: commitId, - Uid: fileObj.Uid, + Uid: fileObj.Uid, }, err } @@ -495,11 +495,11 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") } } - fileKey:=uri - if in.Engine!=api.FileEngine_FILE_ENGINE_LOCAL.String(){ + fileKey := uri + if in.Engine != api.FileEngine_FILE_ENGINE_LOCAL.String() { fileKey = originKey } - fileObj:=&api.Files{ + fileObj := &api.Files{ Uid: f.snowflake.GenerateIDString(), Engine: in.Engine, Bucket: in.Bucket, @@ -509,7 +509,7 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), } - log.Printf("insert %s,%s,%s,%s",fileObj.Uid,fileObj.Bucket,fileObj.Key,fileObj.Engine) + log.Printf("insert %s,%s,%s,%s", fileObj.Uid, fileObj.Bucket, fileObj.Key, fileObj.Engine) err = f.repo.UpsertFile(ctx, fileObj) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") @@ -519,7 +519,7 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a return &api.CompleteMultipartUploadResponse{ Uri: uri, Version: commit, - Uid: fileObj.Uid, + Uid: fileObj.Uid, }, err } @@ -597,15 +597,17 @@ func (f *FileUsecaseImpl) Metadata(ctx context.Context, in *api.FileMetadataRequ version, _ = f.Version(ctx, in.Bucket, originKey, authorId) } - uid:=in.FileId - if uid==""{ - fileObj,err:=f.repo.GetFile(ctx,in.Engine,in.Bucket,in.Key) + uid := in.FileId + bucket := in.Bucket + if uid == "" { + fileObj, err := f.repo.GetFile(ctx, in.Engine, in.Bucket, in.Key) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") } - uid=fileObj.Uid + bucket = fileObj.Bucket + uid = fileObj.Uid } - + // log.Printf("uid:%s,bucket:%s", uid, bucket) return &api.FileMetadataResponse{ Size: file.Size(), ModifyTime: file.ModifyTime(), @@ -614,7 +616,8 @@ func (f *FileUsecaseImpl) Metadata(ctx context.Context, in *api.FileMetadataRequ Name: filepath.Base(in.Key), Etag: fmt.Sprintf("%d-%s", file.Size(), sha256), Version: version, - Uid: uid, + Uid: uid, + Bucket: bucket, }, nil } @@ -665,7 +668,7 @@ func (f *FileUsecaseImpl) checkStatusCode(err error) (int32, codes.Code) { return int32(common.Code_INTERNAL_ERROR), codes.Internal } } -func (f *FileUsecaseImpl)GetFileByID(ctx context.Context, fileId string) (*api.Files, error) { +func (f *FileUsecaseImpl) GetFileByID(ctx context.Context, fileId string) (*api.Files, error) { return f.repo.GetFileById(ctx, fileId) } func (f *FileUsecaseImpl) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) { diff --git a/internal/biz/file/minio.go b/internal/biz/file/minio.go index d9c5fdb..a64f99b 100644 --- a/internal/biz/file/minio.go +++ b/internal/biz/file/minio.go @@ -59,7 +59,7 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au return nil, gosdk.NewError(fmt.Errorf("failed to upload object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upload_object") } - f:=&api.Files{ + f := &api.Files{ Uid: m.localFile.snowflake.GenerateIDString(), Engine: api.FileEngine_name[int32(*api.FileEngine_FILE_ENGINE_MINIO.Enum())], Bucket: in.Bucket, @@ -75,7 +75,7 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au } // log.Printf("upload object:%s,%s", info.Key, info.ChecksumSHA256) - return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID,Uid: f.Uid}, err + return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID, Uid: f.Uid}, err } func (m *MinioUseCase) InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { return m.localFile.InitiateUploadFile(ctx, in) @@ -163,14 +163,18 @@ func (m *MinioUseCase) Metadata(ctx context.Context, in *api.FileMetadataRequest if checksum == "" { checksum = info.UserMetadata["Sha256"] } - fileId:=in.FileId - if fileId==""{ - file,err:=m.localFile.repo.GetFile(ctx,in.Engine,in.Bucket,in.Key) - if err!=nil{ + fileId := in.FileId + bucket := in.Bucket + + if fileId == "" { + file, err := m.localFile.repo.GetFile(ctx, in.Engine, in.Bucket, in.Key) + if err != nil { return nil, gosdk.NewError(fmt.Errorf("failed to get file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") } - fileId=file.Uid + fileId = file.Uid + bucket = file.Bucket } + // log.Printf("fileId:%s,bucket:%s", fileId, bucket) return &api.FileMetadataResponse{Size: info.Size, ContentType: info.ContentType, Version: info.VersionID, @@ -178,7 +182,8 @@ func (m *MinioUseCase) Metadata(ctx context.Context, in *api.FileMetadataRequest Etag: info.ETag, Name: info.Key, Sha256: checksum, - Uid: fileId, + Uid: fileId, + Bucket: bucket, }, nil } func (m *MinioUseCase) Version(ctx context.Context, bucket, key, authorId string) (string, error) { @@ -216,6 +221,6 @@ func (m *MinioUseCase) Delete(ctx context.Context, in *api.DeleteRequest, author func (m *MinioUseCase) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) { return m.localFile.List(ctx, in, authorId) } -func (m *MinioUseCase) GetFileByID(ctx context.Context, fileId string) (*api.Files, error){ +func (m *MinioUseCase) GetFileByID(ctx context.Context, fileId string) (*api.Files, error) { return m.localFile.GetFileByID(ctx, fileId) -} \ No newline at end of file +} diff --git a/internal/service/file.go b/internal/service/file.go index 1041375..0bf138c 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -2,10 +2,7 @@ package service import ( "context" - "crypto/sha256" - "encoding/hex" "fmt" - "log" "net/http" "net/url" "strconv" @@ -131,7 +128,6 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h if err != nil { return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "url_unescape") } - log.Printf("Download file id:%s", in.GetFileId()) in.Key = newKey r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { return f.biz[in.Engine].Download(ctx, req.(*api.DownloadRequest), identity) @@ -142,19 +138,22 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h // return r,err buf, _ := r.([]byte) - shaer := sha256.New() - shaer.Write(buf) - meta, err := f.Metadata(ctx, &api.FileMetadataRequest{Bucket: in.Bucket, Engine: in.Engine, Key: in.Key, Version: in.Version}) + // shaer := sha256.New() + // shaer.Write(buf) + _, err = f.Metadata(ctx, &api.FileMetadataRequest{Bucket: in.Bucket, Engine: in.Engine, Key: in.Key, Version: in.Version}) if err != nil { return nil, err } - rspMd := metadata.Pairs( - gosdk.GetMetadataKey("Content-Length"), fmt.Sprintf("%d", len(buf)), - gosdk.GetMetadataKey("X-File-Sha256"), hex.EncodeToString(shaer.Sum(nil)), - gosdk.GetMetadataKey("Content-Type"), meta.ContentType, - gosdk.GetMetadataKey("X-File-Version"), meta.Version, - ) - _ = grpc.SendHeader(ctx, rspMd) + // log.Printf("bucket,%s", meta.Bucket) + // rspMd := metadata.Pairs( + // gosdk.GetMetadataKey("Content-Length"), fmt.Sprintf("%d", len(buf)), + // gosdk.GetMetadataKey("X-File-Sha256"), hex.EncodeToString(shaer.Sum(nil)), + // gosdk.GetMetadataKey("Content-Type"), meta.ContentType, + // gosdk.GetMetadataKey("X-File-Version"), meta.Version, + // gosdk.GetMetadataKey("X-File-Name"), meta.Key, + // gosdk.GetMetadataKey("X-File-Bucket"), meta.Bucket, + // ) + // _ = grpc.SendHeader(ctx, rspMd) rsp := &httpbody.HttpBody{ ContentType: http.DetectContentType(buf), @@ -299,7 +298,8 @@ func (f *FileService) Metadata(ctx context.Context, in *api.FileMetadataRequest) gosdk.GetMetadataKey("Content-Length"), fmt.Sprintf("%d", rsp.Size), gosdk.GetMetadataKey("X-File-Sha256"), rsp.Sha256, gosdk.GetMetadataKey("X-File-Version"), rsp.Version, - gosdk.GetMetadataKey("Access-Control-Expose-Headers"), "Content-Length, Content-Range, Accept-Ranges, Last-Modified, ETag, Content-Type, X-File-name, X-File-Sha256", + gosdk.GetMetadataKey("X-File-Bucket"), rsp.Bucket, + gosdk.GetMetadataKey("Access-Control-Expose-Headers"), "Content-Length, Content-Range, Accept-Ranges, Last-Modified, ETag, Content-Type, X-File-name, X-File-Sha256,X-File-Bucket,X-File-Version", ) _ = grpc.SendHeader(ctx, rspMd) // if err != nil { From c0e2ca95a1d514df6656f7eac66544c70e659300 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 17 Jun 2024 18:44:29 +0800 Subject: [PATCH 11/16] feat:tranform all auth key to uid --- gateway/endpoint_test.go | 4 +- gateway/http.go | 2 +- gateway/http_test.go | 11 ++ gateway/middlewares.go | 14 +++ gateway/middlewares_test.go | 13 ++ go.mod | 2 +- go.sum | 2 + internal/biz/aksk.go | 8 ++ internal/biz/aksk_test.go | 19 +++ internal/biz/authz.go | 33 ++++- internal/biz/authz_test.go | 51 +++++++- internal/biz/file/file.go | 54 +++++--- internal/biz/file/file_test.go | 156 +++++++++++++----------- internal/biz/file/minio.go | 24 ++-- internal/biz/file/minio_test.go | 23 +++- internal/middleware/auth/ak_test.go | 6 +- internal/middleware/auth/aksk.go | 15 ++- internal/middleware/auth/apikey.go | 43 +++++-- internal/middleware/auth/apikey_test.go | 76 ++++++++++-- internal/middleware/auth/auth.go | 2 +- internal/middleware/auth/auth_test.go | 38 ++++-- internal/middleware/auth/jwt.go | 1 + internal/middleware/auth/jwt_test.go | 6 +- internal/middleware/http.go | 4 +- internal/middleware/middleware.go | 2 +- internal/middleware/middleware_test.go | 3 +- internal/migrate/migrate.go | 1 - internal/service/file.go | 23 ++-- internal/service/file_test.go | 29 +++-- internal/service/service.go | 3 +- internal/service/wire_gen.go | 3 +- internal/wire_gen.go | 7 +- 32 files changed, 498 insertions(+), 180 deletions(-) diff --git a/gateway/endpoint_test.go b/gateway/endpoint_test.go index 65da7f9..eba54f2 100644 --- a/gateway/endpoint_test.go +++ b/gateway/endpoint_test.go @@ -11,7 +11,7 @@ import ( ) func TestRequest(t *testing.T) { - go example.Run(":12138") + go example.Run(":12148") time.Sleep(time.Second * 3) c.Convey("test request", t, func() { // request := GrpcRequestImpl{ @@ -21,7 +21,7 @@ func TestRequest(t *testing.T) { // Ctx: context.Background(), // } request := NewGrpcRequest(context.Background(), nil, nil, "helloworld.Greeter/SayHello", WithIn(&v1.HelloRequest{Msg: "begonia"}), WithOut(&v1.HelloReply{})) - pool := NewGrpcConnPool("127.0.0.1:12138") + pool := NewGrpcConnPool("127.0.0.1:12148") endpoint := NewEndpoint(pool) reply, metadata, err := endpoint.Request(request) c.So(err, c.ShouldBeNil) diff --git a/gateway/http.go b/gateway/http.go index a17b0af..02ed5e1 100644 --- a/gateway/http.go +++ b/gateway/http.go @@ -382,7 +382,7 @@ func (h *HttpEndpointImpl) RegisterHandlerClient(ctx context.Context, pd Protobu item := item // log.Printf("register endpoint %s: %s %v", strings.ToUpper(item.HttpMethod), item.HttpUri, item.Pattern) mux.Handle(strings.ToUpper(item.HttpMethod), item.Pattern, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - if req.Header.Get("accept") == "" { + if req.Header.Get("accept") == "" || req.Header.Get("accept") == "*/*" { req.Header.Set("accept", "application/json") } ctx, cancel := context.WithCancel(req.Context()) diff --git a/gateway/http_test.go b/gateway/http_test.go index 1ca1c2c..6bb8c5d 100644 --- a/gateway/http_test.go +++ b/gateway/http_test.go @@ -194,6 +194,17 @@ func testRequestGet(t *testing.T) { c.So(err, c.ShouldBeNil) c.So(resp2.StatusCode, c.ShouldEqual, http.StatusNotImplemented) + // test appkey + url = fmt.Sprintf("http://127.0.0.1:%d/api/v1/example/world?msg=hello", gwPort) + r, err = http.NewRequest(http.MethodGet, url, nil) + r.Header.Set(XApiKey, "12345678") + c.So(err, c.ShouldBeNil) + + resp, err = http.DefaultClient.Do(r) + + c.So(err, c.ShouldBeNil) + c.So(resp.StatusCode, c.ShouldEqual, http.StatusOK) + }) } func testCors(t *testing.T) { diff --git a/gateway/middlewares.go b/gateway/middlewares.go index 60b07bd..46e8e89 100644 --- a/gateway/middlewares.go +++ b/gateway/middlewares.go @@ -35,6 +35,8 @@ const ( XProtocol = "x-http-protocol" XHttpURI = "x-http-uri" XIdentity = "x-identity" + XApiKey = "x-api-key" + XIdentityType = "x-identity-type" ) func preflightHandler(w http.ResponseWriter, _ *http.Request) { @@ -114,17 +116,25 @@ func IncomingHeadersToMetadata(ctx context.Context, req *http.Request) metadata. md.Set(gosdk.GetMetadataKey(XRequestID), reqID) xuid := md.Get(XUID) accessKey := md.Get(XAccessKey) + apikey:=md.Get(XApiKey) author := "" + idType := gosdk.UidType if len(xuid) > 0 { author = xuid[0] } if author == "" && len(accessKey) > 0 { author = accessKey[0] + idType = gosdk.AccessKeyType + } + if author == ""&& len(apikey)>0{ + author = apikey[0] + idType = gosdk.ApiKeyType } if author == "" { return md } md.Set(XIdentity, author) + md.Set(XIdentityType, idType) return md } @@ -216,6 +226,10 @@ func clientMessageFromCode(code codes.Code) string { switch code { case codes.ResourceExhausted: return "The requested resource size exceeds the server limit." + case codes.NotFound: + return "The requested resource is not found." + case codes.AlreadyExists: + return "The requested resource already exists." default: return "Unknown error" diff --git a/gateway/middlewares_test.go b/gateway/middlewares_test.go index 89723ea..b23d342 100644 --- a/gateway/middlewares_test.go +++ b/gateway/middlewares_test.go @@ -8,6 +8,7 @@ import ( hello "github.com/begonia-org/go-sdk/api/example/v1" c "github.com/smartystreets/goconvey/convey" "google.golang.org/grpc" + "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" ) @@ -23,6 +24,18 @@ func (r *responseWriter) Write([]byte) (int, error) { } func (r *responseWriter) WriteHeader(int) { +} +func TestClientMessageFromCode(t *testing.T){ + c.Convey("TestClientMessageFromCode",t,func(){ + msg:=clientMessageFromCode(codes.NotFound) + c.So(msg,c.ShouldContainSubstring,"not found") + msg = clientMessageFromCode(codes.ResourceExhausted) + c.So(msg,c.ShouldContainSubstring,"resource size exceeds") + msg = clientMessageFromCode(codes.AlreadyExists) + c.So(msg,c.ShouldContainSubstring,"already exists") + msg = clientMessageFromCode(codes.DataLoss) + c.So(msg,c.ShouldContainSubstring,"Unknown error") + }) } func TestLoggerMiddlewares(t *testing.T) { mid :=NewLoggerMiddleware(Log) diff --git a/go.mod b/go.mod index 453bcd4..a6c722b 100644 --- a/go.mod +++ b/go.mod @@ -80,7 +80,7 @@ require ( require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38 + github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 diff --git a/go.sum b/go.sum index 9a486da..c37dcda 100644 --- a/go.sum +++ b/go.sum @@ -27,6 +27,8 @@ github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12 h1:4mZUWCrcl9gI github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38 h1:jY6dVk3lzs8gHQBqLfqdZvz8L5u/MwGx9baTJK8Fj7Y= github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f h1:1roWosaPjA7vE5CnnF6irDWt05SLD57bsHLun4HYeLU= +github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= diff --git a/internal/biz/aksk.go b/internal/biz/aksk.go index 0bf8132..60afdd7 100644 --- a/internal/biz/aksk.go +++ b/internal/biz/aksk.go @@ -117,3 +117,11 @@ func (a *AccessKeyAuth) GetAppid(ctx context.Context, accessKey string) (string, } return appid, nil } + +func (a *AccessKeyAuth) GetAppOwner(ctx context.Context, accessKey string) (string, error) { + app, err := a.app.Get(ctx, accessKey) + if err != nil { + return "", gosdk.NewError(err, int32(api.APPSvrCode_APP_UNKNOWN), codes.Unauthenticated, "app_owner") + } + return app.Owner, nil +} \ No newline at end of file diff --git a/internal/biz/aksk_test.go b/internal/biz/aksk_test.go index 4519358..ef076b3 100644 --- a/internal/biz/aksk_test.go +++ b/internal/biz/aksk_test.go @@ -30,6 +30,7 @@ import ( var akskAccess = "" var akskSecret = "" var akskAppid = "" +var akskOwner="" func newGatewayRequest() (*gosdk.GatewayRequest, error) { signer := gosdk.NewAppAuthSigner(akskAccess, akskSecret) @@ -75,6 +76,7 @@ func testGetSecret(t *testing.T) { akskSecret, _ = utils.GenerateRandomString(62) akskAppid = snk.GenerateIDString() appName := fmt.Sprintf("app-AKSK-%s", time.Now().Format("20060102150405")) + akskOwner = akskAppid err := repo.Add(context.TODO(), &api.Apps{ Appid: akskAppid, AccessKey: akskAccess, @@ -85,6 +87,8 @@ func testGetSecret(t *testing.T) { Description: "test", CreatedAt: timestamppb.New(time.Now()), UpdatedAt: timestamppb.New(time.Now()), + Owner: akskOwner, + }) if err != nil { @@ -116,6 +120,20 @@ func testGetAPPID(t *testing.T) { c.So(appid, c.ShouldBeEmpty) }) } +func testGetAppOwner(t *testing.T) { + aksk := newAKSK() + c.Convey("get app owner", t, func() { + appid, err := aksk.GetAppOwner(context.TODO(), akskAccess) + c.So(err, c.ShouldBeNil) + c.So(appid, c.ShouldEqual, akskOwner) + }) + + c.Convey("get app owner fail", t, func() { + appid, err := aksk.GetAppOwner(context.TODO(), "dddddeeedwecccwcqdq") + c.So(err, c.ShouldNotBeNil) + c.So(appid, c.ShouldBeEmpty) + }) +} func testIfNeedValidate(t *testing.T) { c.Convey("test if need validate", t, func() { ok := biz.IfNeedValidate(context.TODO(), akskAccess) @@ -230,6 +248,7 @@ func testValidator(t *testing.T) { func TestAKSK(t *testing.T) { t.Run("get secret", testGetSecret) t.Run("get appid", testGetAPPID) + t.Run("get app owner", testGetAppOwner) t.Run("validator", testValidator) t.Run("if need validate", testIfNeedValidate) } diff --git a/internal/biz/authz.go b/internal/biz/authz.go index 2cd6891..3af2233 100644 --- a/internal/biz/authz.go +++ b/internal/biz/authz.go @@ -33,10 +33,11 @@ type AuthzUsecase struct { authCrypto *crypto.UsersAuth config *config.Config user UserRepo + app AppRepo } -func NewAuthzUsecase(repo AuthzRepo, user UserRepo, log logger.Logger, crypto *crypto.UsersAuth, config *config.Config) *AuthzUsecase { - return &AuthzUsecase{repo: repo, log: log, authCrypto: crypto, config: config, user: user} +func NewAuthzUsecase(repo AuthzRepo, user UserRepo, app AppRepo, log logger.Logger, crypto *crypto.UsersAuth, config *config.Config) *AuthzUsecase { + return &AuthzUsecase{repo: repo, log: log, authCrypto: crypto, config: config, user: user, app: app} } func (u *AuthzUsecase) DelToken(ctx context.Context, key string) error { @@ -166,5 +167,33 @@ func (u *AuthzUsecase) Logout(ctx context.Context, req *api.LogoutAPIRequest) er return gosdk.NewError(err, int32(common.Code_AUTH_ERROR), codes.Internal, "add_black_list") } return nil +} +func (u *AuthzUsecase) GetIdentity(ctx context.Context, idType string, identity string) (string, error) { + switch idType { + case gosdk.UidType: + return identity, nil + case gosdk.AccessKeyType: + app, err := u.app.Get(ctx, identity) + if err != nil { + return "", gosdk.NewError(err, int32(common.Code_AUTH_ERROR), codes.Internal, "app_query") + } + return app.Owner, nil + case gosdk.ApiKeyType: + apikey := u.config.GetAdminAPIKey() + if apikey != identity { + return "", gosdk.NewError(pkg.ErrAPIKeyNotMatch, int32(common.Code_AUTH_ERROR), codes.Unauthenticated, "api_key") + } + // 登陆验证 + key, iv := u.config.GetAesConfig() + adminName := u.config.GetDefaultAdminName() + account, _ := tiga.EncryptAES([]byte(key), adminName, iv) + user, err := u.user.Get(ctx, account) + if err != nil { + return "", gosdk.NewError(err, int32(common.Code_AUTH_ERROR), codes.Internal, "user_query") + } + return user.Uid, nil + default: + return "", gosdk.NewError(pkg.ErrIdentityMissing, int32(common.Code_AUTH_ERROR), codes.InvalidArgument, "identity_type") + } } diff --git a/internal/biz/authz_test.go b/internal/biz/authz_test.go index 0667f73..6f1f298 100644 --- a/internal/biz/authz_test.go +++ b/internal/biz/authz_test.go @@ -24,10 +24,12 @@ import ( "github.com/begonia-org/begonia/internal/data" "github.com/begonia-org/begonia/internal/pkg" cfg "github.com/begonia-org/begonia/internal/pkg/config" + gosdk "github.com/begonia-org/go-sdk" "github.com/begonia-org/begonia/internal/pkg/crypto" "github.com/begonia-org/begonia/internal/pkg/utils" v1 "github.com/begonia-org/go-sdk/api/user/v1" + app "github.com/begonia-org/go-sdk/api/app/v1" "github.com/spark-lence/tiga" "google.golang.org/grpc/metadata" @@ -48,8 +50,9 @@ func newAuthzBiz() *biz.AuthzUsecase { user := data.NewUserRepo(config, gateway.Log) cnf := cfg.NewConfig(config) crypto := crypto.NewUsersAuth(cnf) + app := data.NewAppRepo(config, gateway.Log) - return biz.NewAuthzUsecase(repo, user, gateway.Log, crypto, cnf) + return biz.NewAuthzUsecase(repo, user,app, gateway.Log, crypto, cnf) } func testAuthSeed(t *testing.T) { @@ -307,6 +310,51 @@ func testDelToken(t *testing.T) { c.So(err, c.ShouldBeNil) }) } +func testGetIdentity(t *testing.T){ + authzBiz := newAuthzBiz() + c.Convey("test get identity", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + config := config.ReadConfig(env) + appRepo := data.NewAppRepo(config, gateway.Log) + patch:=gomonkey.ApplyMethodReturn(appRepo,"Get",&app.Apps{Owner: "12345567"},nil) + defer patch.Reset() + identity, err := authzBiz.GetIdentity(context.Background(), gosdk.AccessKeyType,"123456") + patch.Reset() + c.So(err, c.ShouldBeNil) + c.So(identity, c.ShouldEqual, "12345567") + identity, err = authzBiz.GetIdentity(context.Background(), gosdk.UidType,"123456") + + c.So(err, c.ShouldBeNil) + c.So(identity, c.ShouldEqual, "123456") + cnf:=cfg.NewConfig(config) + apikey:=cnf.GetAdminAPIKey() + identity, err = authzBiz.GetIdentity(context.Background(), gosdk.UidType,"123456") + c.So(err, c.ShouldBeNil) + c.So(identity, c.ShouldEqual, "123456") + + + identity, err = authzBiz.GetIdentity(context.Background(), gosdk.ApiKeyType,apikey) + c.So(err, c.ShouldBeNil) + c.So(identity, c.ShouldNotBeEmpty) + _, err = authzBiz.GetIdentity(context.Background(), gosdk.ApiKeyType,"123") + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,pkg.ErrAPIKeyNotMatch.Error()) + patch2:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("error")) + defer patch2.Reset() + _, err = authzBiz.GetIdentity(context.Background(), gosdk.ApiKeyType,apikey) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,"error") + patch2.Reset() + + _, err = authzBiz.GetIdentity(context.Background(), "gosdk.ApiKeyType",apikey) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,pkg.ErrIdentityMissing.Error()) + }) + +} func testPutBlackList(t *testing.T) { authzBiz := newAuthzBiz() c.Convey("test put black list", t, func() { @@ -321,6 +369,7 @@ func testPutBlackList(t *testing.T) { func TestAuthz(t *testing.T) { t.Run("test auth seed", testAuthSeed) t.Run("test login", testLogin) + t.Run("test get identity",testGetIdentity) t.Run("test logout", testLogout) t.Run("test del token", testDelToken) t.Run("test put black list", testPutBlackList) diff --git a/internal/biz/file/file.go b/internal/biz/file/file.go index 7aae196..8ef18d9 100644 --- a/internal/biz/file/file.go +++ b/internal/biz/file/file.go @@ -50,7 +50,7 @@ type FileUsecase interface { Version(ctx context.Context, bucket, key, authorId string) (string, error) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) - MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) + MakeBucket(ctx context.Context, in *api.MakeBucketRequest,authorId string) (*api.MakeBucketResponse, error) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) GetFileByID(ctx context.Context, fileId string) (*api.Files, error) } @@ -131,14 +131,14 @@ func getSHA256(data []byte) string { func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId string, authorEmail string) (commitId string, err error) { repo, err := git.PlainInit(dir, false) if err != nil && err != git.ErrRepositoryAlreadyExists { - return "", err + return "", fmt.Errorf("version plain init err:%w",err) } // 如果仓库已存在,则打开它 if err == git.ErrRepositoryAlreadyExists { repo, err = git.PlainOpen(dir) if err != nil { - return "", err + return "", fmt.Errorf("version plain open err:%w",err) } } @@ -152,7 +152,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin // 工作树 w, err := repo.Worktree() if err != nil { - return "", err + return "", fmt.Errorf("version work tree err:%w",err) } defer func() { if p := recover(); p != nil { @@ -166,7 +166,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin // 添加文件到暂存区 _, err = w.Add(filename) if err != nil { - return "", err + return "", fmt.Errorf("add file err:%w",err) } // 创建提交 @@ -174,7 +174,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin Author: author, }) if err != nil && !goErr.Is(err, git.ErrEmptyCommit) { - return "", err + return "", fmt.Errorf("commit file err:%w",err) } // 空提交处理 if goErr.Is(err, git.ErrEmptyCommit) { @@ -189,7 +189,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin obj, err := repo.CommitObject(commit) if err != nil { - return "", err + return "", fmt.Errorf("get commit object err:%w",err) } return obj.ID().String(), nil @@ -216,18 +216,33 @@ func (f *FileUsecaseImpl) checkIn(key string) (string, error) { } return key, nil } -func (f *FileUsecaseImpl) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { +func (f *FileUsecaseImpl) MakeBucket(ctx context.Context, in *api.MakeBucketRequest,authorId string) (*api.MakeBucketResponse, error) { saveDir := filepath.Join(f.config.GetUploadDir(), in.Bucket) if err := os.MkdirAll(saveDir, 0755); err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket") } + if in.EnableVersion{ + _,_=os.Create(filepath.Join(saveDir, ".gitkeep")) + _,err:=f.commitFile(saveDir, ".gitkeep", authorId, "fs@begonia.com") + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("init bucket version err:%w",err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") + + } + } return &api.MakeBucketResponse{}, nil } func (f *FileUsecaseImpl) checkBucket(bucket string) bool { saveDir := filepath.Join(f.config.GetUploadDir(), bucket) return pathExists(saveDir) } - +func (f *FileUsecaseImpl)isEnableVersion(bucket string)bool{ + path:=filepath.Join(f.config.GetUploadDir(), bucket) + repo, err := git.PlainOpen(path) + if err!=nil||repo==nil{ + return false + } + return true +} // Upload uploads a file. // // The file is saved in the directory specified by the key. @@ -242,7 +257,8 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, if err != nil { return nil, err } - in.Key = filepath.Join(authorId, key) + // in.Key = filepath.Join(authorId, key) + in.Key = key // log.Printf("key:%s", in.Key) filename := filepath.Base(in.Key) if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok { @@ -261,9 +277,9 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, } defer file.Close() defer func() { - if err != nil { - os.Remove(filePath) - } + // if err != nil { + // os.Remove(filePath) + // } }() _, err = file.Write(in.Content) if err != nil { @@ -281,7 +297,7 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, } commitId := "" - if in.UseVersion { + if f.isEnableVersion(in.Bucket) { commitId, err = f.commitFile(saveDir, filename, authorId, "fs@begonia.com") if err != nil { err = gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") @@ -303,6 +319,7 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, if err != nil { return nil, err } + log.Printf("上传文件:%s成功", filePath) return &api.UploadFileResponse{ Uri: uri, Version: commitId, @@ -448,11 +465,12 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a if err != nil { return nil, err } - if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok { + if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok && in.Engine==api.FileEngine_FILE_ENGINE_LOCAL.String() { return nil, gosdk.NewError(pkg.ErrBucketNotFound, int32(api.FileSvrStatus_FILE_INVALIDATE_BUCKET_ERR), codes.NotFound, "bucket_not_found") } - in.Key = filepath.Join(authorId, key) + // in.Key = filepath.Join(authorId, key) + in.Key = key partsDir := f.getPartsDir(in.UploadId) if !pathExists(partsDir) { err := gosdk.NewError(fmt.Errorf("%s:%s", in.UploadId, pkg.ErrUploadIdNotFound.Error()), int32(api.FileSvrStatus_FILE_NOT_FOUND_UPLOADID_ERR), codes.NotFound, "upload_id_not_found") @@ -489,8 +507,8 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a } commit := "" - if in.UseVersion { - commit, err = f.commitFile(saveDir, filename, authorId, "begonia@begonia.com") + if f.isEnableVersion(in.Bucket)&& in.Engine==api.FileEngine_FILE_ENGINE_LOCAL.String() { + commit, err = f.commitFile(saveDir, filename, authorId, "fs@begonia.com") if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") } diff --git a/internal/biz/file/file_test.go b/internal/biz/file/file_test.go index 8f25518..a3d4cb3 100644 --- a/internal/biz/file/file_test.go +++ b/internal/biz/file/file_test.go @@ -63,6 +63,7 @@ type FileDownloadTestCase struct { var fileAuthor = "" var fileSha256 = "" +var fileId = "" var uploadId = "" var bucket = "" var versions map[string]string = map[string]string{} @@ -143,7 +144,8 @@ func testMkBucket(t *testing.T) { c.Convey("test make bucket success", t, func() { rsp, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ Bucket: bucket, - }) + EnableVersion: true, + }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) }) @@ -152,9 +154,21 @@ func testMkBucket(t *testing.T) { defer patch.Reset() _, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ Bucket: "", - }) + },fileAuthor) + patch.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "mkdir error") + + patch1:=gomonkey.ApplyFuncReturn(git.PlainInit, nil, fmt.Errorf("init error")) + defer patch1.Reset() + _, err = fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ + Bucket: "test-2", + EnableVersion: true, + },fileAuthor) + patch1.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "init error") + }) } func testPutFile(t *testing.T) { @@ -193,8 +207,8 @@ func testPutFile(t *testing.T) { tmp: tmp, useVersion: false, key: "test/upload.test1", - expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test1"), - expectUri: fileAuthor + "/test/upload.test1", + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test1"), + expectUri: "test/upload.test1", expectErr: nil, author: fileAuthor, bucket: bucket, @@ -204,8 +218,8 @@ func testPutFile(t *testing.T) { tmp: tmp, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), - expectUri: fileAuthor + "/test/upload.test2", + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test2"), + expectUri: "test/upload.test2", expectErr: nil, author: fileAuthor, bucket: bucket, @@ -215,8 +229,8 @@ func testPutFile(t *testing.T) { tmp: tmp, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), - expectUri: fileAuthor + "/test/upload.test2", + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test2"), + expectUri: "test/upload.test2", expectErr: nil, author: fileAuthor, bucket: bucket, @@ -226,8 +240,8 @@ func testPutFile(t *testing.T) { tmp: tmp3, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), - expectUri: fileAuthor + "/test/upload.test2", + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test2"), + expectUri: "test/upload.test2", expectErr: nil, author: fileAuthor, bucket: bucket, @@ -237,8 +251,8 @@ func testPutFile(t *testing.T) { tmp: tmp, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), - expectUri: fileAuthor + "/test/upload.test2", + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test2"), + expectUri: "test/upload.test2", expectErr: nil, author: fileAuthor, bucket: bucket, @@ -292,7 +306,6 @@ func testPutFile(t *testing.T) { Key: _case.key, Content: _case.tmp.content, ContentType: _case.tmp.contentType, - UseVersion: _case.useVersion, Sha256: _case.tmp.sha256, Bucket: _case.bucket, }, _case.author) @@ -307,6 +320,7 @@ func testPutFile(t *testing.T) { c.So(err, c.ShouldBeNil) c.So(_case.tmp.sha256, c.ShouldEqual, expectSum) c.So(rsp.Uri, c.ShouldEqual, _case.expectUri) + fileId = rsp.Uid if _case.useVersion { // t.Logf("upload title:%s,version:%s,sha256:%s", _case.title, rsp.Version, _case.tmp.sha256) c.So(rsp.Version, c.ShouldNotBeEmpty) @@ -323,9 +337,9 @@ func testPutFile(t *testing.T) { for v, sha := range versions { t.Logf("version:%s,sha256:%s", v, sha) } - fileAuthor2 := fmt.Sprintf("tester-2-%s", time.Now().Format("20060102150405")) + bucket2 := fmt.Sprintf("tester-2-%s", time.Now().Format("20060102150405")) - filePath := filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor2) + filePath := filepath.Join(cnf.GetUploadDir(), bucket2) file, err := os.Create(filePath) if err != nil { t.Error(err) @@ -335,10 +349,9 @@ func testPutFile(t *testing.T) { Key: "test/upload.test5", Content: tmp.content, ContentType: tmp.contentType, - UseVersion: true, Sha256: tmp.sha256, - Bucket: bucket, - }, fileAuthor2) + Bucket: bucket2, + }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(rsp, c.ShouldBeNil) @@ -348,7 +361,6 @@ func testPutFile(t *testing.T) { Key: "test/.", Content: tmp.content, ContentType: tmp.contentType, - UseVersion: true, Sha256: tmp.sha256, Bucket: bucket, }, fileAuthor) @@ -363,7 +375,6 @@ func testPutFile(t *testing.T) { Key: "test/upload.test6", Content: nil, ContentType: tmp.contentType, - UseVersion: true, Sha256: tmp.sha256, Bucket: bucket, }, fileAuthor3) @@ -378,7 +389,6 @@ func testPutFile(t *testing.T) { Key: "test/upload.test6", Content: nil, ContentType: tmp.contentType, - UseVersion: true, Sha256: tmp.sha256, Bucket: bucket, }, fileAuthor3) @@ -399,7 +409,7 @@ func testDownload(t *testing.T) { { title: "download success", exceptErr: nil, - key: fileAuthor + "/test/upload.test1", + key: "test/upload.test1", useVersion: false, version: "", author: fileAuthor, @@ -408,7 +418,7 @@ func testDownload(t *testing.T) { { title: "download with version1", exceptErr: nil, - key: fileAuthor + "/test/upload.test2", + key: "test/upload.test2", useVersion: true, version: versionsKeys[0], author: fileAuthor, @@ -417,7 +427,7 @@ func testDownload(t *testing.T) { { title: "download with version2", exceptErr: nil, - key: fileAuthor + "/test/upload.test2", + key: "test/upload.test2", useVersion: true, version: versionsKeys[1], author: fileAuthor, @@ -426,7 +436,7 @@ func testDownload(t *testing.T) { { title: "download with version3", exceptErr: nil, - key: fileAuthor + "/test/upload.test2", + key: "test/upload.test2", useVersion: true, version: versionsKeys[2], author: fileAuthor, @@ -435,7 +445,7 @@ func testDownload(t *testing.T) { { title: "download with latest version", exceptErr: nil, - key: fileAuthor + "/test/upload.test2", + key: "test/upload.test2", useVersion: true, version: "", author: fileAuthor, @@ -445,7 +455,7 @@ func testDownload(t *testing.T) { { title: "download fail with invalidate key", exceptErr: fmt.Errorf("%s", "no such file or directory"), - key: fileAuthor + "/test/upload_not_exist", + key: "test/upload_not_exist", useVersion: false, version: "", author: fileAuthor, @@ -454,7 +464,7 @@ func testDownload(t *testing.T) { { title: "download fail with version and invalidate key", exceptErr: fmt.Errorf("%s", "file not found"), - key: fileAuthor + "/test/upload_not_exist", + key: "test/upload_not_exist", useVersion: true, version: "latest", author: fileAuthor, @@ -468,6 +478,7 @@ func testDownload(t *testing.T) { Key: _case.key, Bucket: bucket, Version: _case.version, + }, _case.author) if _case.exceptErr != nil { c.So(err, c.ShouldNotBeNil) @@ -485,7 +496,7 @@ func testDownload(t *testing.T) { } c.Convey("test download fail", t, func() { _, err := fileBiz.Download(context.TODO(), &api.DownloadRequest{ - Key: "/" + fileAuthor + "/test/upload.test1", + Key: "/test/upload.test1", Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) @@ -498,28 +509,28 @@ func testDownload(t *testing.T) { } config := config.ReadConfig(env) cnf := cfg.NewConfig(config) - filePath := filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2") + filePath := filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test2") t.Logf("filepath:%s", filePath) reader, err := file.NewFileVersionReader(filePath, "latest") c.So(err, c.ShouldBeNil) patch := gomonkey.ApplyMethodReturn(reader, "Reader", nil, fmt.Errorf("reader error")) defer patch.Reset() _, err = fileBiz.Download(context.TODO(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.test2", + Key: "test/upload.test2", Bucket: bucket, Version: "latest", }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "reader error") patch.Reset() - file, err := file.NewFileVersionReader(filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), "latest") + file, err := file.NewFileVersionReader(filepath.Join(cnf.GetUploadDir(), bucket, "test", "upload.test2"), "latest") c.So(err, c.ShouldBeNil) ioReader, err := file.Reader() c.So(err, c.ShouldBeNil) patch2 := gomonkey.ApplyMethodReturn(ioReader, "Read", 0, fmt.Errorf("error read file")) defer patch2.Reset() _, err = fileBiz.Download(context.TODO(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.test2", + Key: "test/upload.test2", Version: "latest", Bucket: bucket, }, fileAuthor) @@ -768,7 +779,6 @@ func testCompleteMultipartUploadFile(t *testing.T) { rsp, err := fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ Key: "test/upload.parts.test1", UploadId: uploadId, - UseVersion: true, Sha256: bigFileSha256, Bucket: bucket, Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), @@ -821,7 +831,6 @@ func testCompleteMultipartUploadFile(t *testing.T) { _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ Key: "test/upload.parts.test2", UploadId: uploadId2, - UseVersion: true, Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) @@ -880,11 +889,11 @@ func testCompleteMultipartUploadFile(t *testing.T) { _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ Key: "test/upload.parts.test2", UploadId: uploadId3, - UseVersion: true, Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) patch2.Reset() - + t.Logf("error:%v", cases.err) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, cases.err.Error()) @@ -896,7 +905,7 @@ func testFileMeta(t *testing.T) { fileBiz := newFileBiz() c.Convey("test file metadata", t, func() { meta, err := fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "", Bucket: bucket, Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), @@ -907,7 +916,7 @@ func testFileMeta(t *testing.T) { c.So(meta.Sha256, c.ShouldEqual, bigFileSha256) meta, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), @@ -931,13 +940,13 @@ func testFileMeta(t *testing.T) { } conf := config.ReadConfig(env) cnf := cfg.NewConfig(conf) - filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir(fileAuthor+"/test/upload.parts.test1")) - filePath = filepath.Join(filePath, filepath.Base(fileAuthor+"/test/upload.parts.test1")) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir("test/upload.parts.test1")) + filePath = filepath.Join(filePath, filepath.Base("test/upload.parts.test1")) patch := gomonkey.ApplyFuncReturn(file.NewFileVersionReader, nil, fmt.Errorf("file NewFileVersionReader error")) defer patch.Reset() _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, fileAuthor) @@ -950,7 +959,7 @@ func testFileMeta(t *testing.T) { patch2 := gomonkey.ApplyMethodReturn(reader, "Reader", nil, fmt.Errorf("file Reader error")) defer patch2.Reset() _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, fileAuthor) @@ -960,7 +969,7 @@ func testFileMeta(t *testing.T) { patch3 := gomonkey.ApplyFuncReturn(io.Copy, int64(0), fmt.Errorf("io.copy error")) defer patch3.Reset() - _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{Key: fileAuthor + "/test/upload.parts.test1", + _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "io.copy error") @@ -969,7 +978,7 @@ func testFileMeta(t *testing.T) { patch4:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) defer patch4.Reset() _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, fileAuthor) @@ -988,7 +997,7 @@ func testVersionReader(t *testing.T) { cnf := cfg.NewConfig(config) c.Convey("test version reader", t, func() { root := cnf.GetUploadDir() - path := filepath.Join(root, bucket, fileAuthor, "test", "upload.parts.test1") + path := filepath.Join(root, bucket, "test", "upload.parts.test1") reader, err := file.NewFileVersionReader(path, "") c.So(err, c.ShouldBeNil) c.So(reader, c.ShouldNotBeNil) @@ -1007,13 +1016,13 @@ func testVersionReader(t *testing.T) { c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) patch := gomonkey.ApplyFuncReturn(file.NewFileVersionReader, nil, fmt.Errorf("file NewFileVersionReader error")) defer patch.Reset() - _, err = fileBiz.Version(context.Background(), bucket, fileAuthor+"/test/upload.parts.test1", fileAuthor) + _, err = fileBiz.Version(context.Background(), bucket, "test/upload.parts.test1", fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file NewFileVersionReader error") patch.Reset() patch2 := gomonkey.ApplyFuncReturn(git.PlainOpen, nil, fmt.Errorf("git PlainOpen error")) defer patch2.Reset() - _, err = file.NewFileVersionReader(fileAuthor+"/test/upload.parts.test1", "latest") + _, err = file.NewFileVersionReader("test/upload.parts.test1", "latest") c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "git PlainOpen error") patch2.Reset() @@ -1021,14 +1030,14 @@ func testVersionReader(t *testing.T) { patch3 := gomonkey.ApplyFuncReturn((*git.Repository).Head, nil, fmt.Errorf("head error")) defer patch3.Reset() root := cnf.GetUploadDir() - path := filepath.Join(root, bucket, fileAuthor, "test", "upload.parts.test2") + path := filepath.Join(root, bucket, "test", "upload.parts.test2") _, err = file.NewFileVersionReader(path, "latest") c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "head error") patch3.Reset() - filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir(fileAuthor+"/test/upload.parts.test1")) - filePath = filepath.Join(filePath, filepath.Base(fileAuthor+"/test/upload.parts.test1")) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir("test/upload.parts.test1")) + filePath = filepath.Join(filePath, filepath.Base("test/upload.parts.test1")) reader, err := file.NewFileVersionReader(filePath, "latest") c.So(err, c.ShouldBeNil) cases := []struct { @@ -1091,7 +1100,7 @@ func testDownloadRange(t *testing.T) { rangeEndAt = int64(fileSize) - 1 } data, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, rangeStartAt, rangeEndAt, fileAuthor) @@ -1103,7 +1112,7 @@ func testDownloadRange(t *testing.T) { shaer2 := sha256.New() data, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, 0, 0, fileAuthor) @@ -1113,7 +1122,7 @@ func testDownloadRange(t *testing.T) { shaer3 := sha256.New() data, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "", Bucket: bucket, }, 0, 0, fileAuthor) @@ -1123,7 +1132,7 @@ func testDownloadRange(t *testing.T) { }) c.Convey("test download range parts file fail", t, func() { _, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.parts.test40", + Key: "test/upload.parts.test40", Version: "lasted", Bucket: bucket, }, 0, 1024, fileAuthor) @@ -1145,7 +1154,7 @@ func testDownloadRange(t *testing.T) { c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidRange.Error()) _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, 1024, 0, fileAuthor) @@ -1167,15 +1176,15 @@ func testDownloadRange(t *testing.T) { } conf := config.ReadConfig(env) cnf := cfg.NewConfig(conf) - filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir(fileAuthor+"/test/upload.parts.test1")) - filePath = filepath.Join(filePath, filepath.Base(fileAuthor+"/test/upload.parts.test1")) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir("test/upload.parts.test1")) + filePath = filepath.Join(filePath, filepath.Base("test/upload.parts.test1")) reader, err := file.NewFileVersionReader(filePath, "latest") c.So(err, c.ShouldBeNil) patch2 := gomonkey.ApplyMethodReturn(reader, "ReadAt", 0, fmt.Errorf("file readAt error")) defer patch2.Reset() _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ - Key: fileAuthor + "/test/upload.parts.test1", + Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, }, 0, 1024, fileAuthor) @@ -1223,22 +1232,22 @@ func testDelete(t *testing.T) { c.Convey("test delete file fail", t, func() { patch := gomonkey.ApplyFuncReturn(file.NewFileReader, nil, fmt.Errorf("file not found")) defer patch.Reset() - _, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.deleted", Bucket: bucket}, fileAuthor) + _, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "test/upload.parts.deleted", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file not found") patch.Reset() - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "not found") - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "/" + fileAuthor + "/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key:"/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) patch2 := gomonkey.ApplyFuncReturn(os.RemoveAll, fmt.Errorf("remove all error")) defer patch2.Reset() - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.test1", Bucket: bucket}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "test/upload.parts.test1", Bucket: bucket}, fileAuthor) patch2.Reset() c.So(err, c.ShouldNotBeNil) @@ -1246,12 +1255,12 @@ func testDelete(t *testing.T) { }) c.Convey("test delete file success", t, func() { - rsp, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.test1", Bucket: bucket}, fileAuthor) + rsp, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "test/upload.parts.test1", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) - _, err = os.Stat(filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.parts.test1")) + _, err = os.Stat(filepath.Join(cnf.GetUploadDir(), "test", "upload.parts.test1")) c.So(err, c.ShouldNotBeNil) - _, err = os.Stat(filepath.Join(cnf.GetUploadDir(), "parts", fileAuthor+"/test/upload.parts.test1")) + _, err = os.Stat(filepath.Join(cnf.GetUploadDir(), "parts", "test/upload.parts.test1")) c.So(err, c.ShouldNotBeNil) }) @@ -1266,6 +1275,7 @@ func testUploadVersionCommitErr(t *testing.T) { if err != nil { t.Error(err) } + t.Logf("test upload version commit error start,%s", tmp2.path) defer os.Remove(tmp2.path) cases := []struct { patch interface{} @@ -1306,7 +1316,6 @@ func testUploadVersionCommitErr(t *testing.T) { Key: "test/upload_version.test1", Content: tmp2.content, ContentType: tmp2.contentType, - UseVersion: true, Sha256: tmp2.sha256, Bucket: bucket, }, fileAuthor2) @@ -1317,13 +1326,12 @@ func testUploadVersionCommitErr(t *testing.T) { } patch := gomonkey.ApplyFuncReturn(git.PlainInit, nil, git.ErrRepositoryAlreadyExists) - patch = patch.ApplyFuncReturn(git.PlainOpen, nil, fmt.Errorf("open error")) + patch = patch.ApplyFuncSeq(git.PlainOpen, []gomonkey.OutputCell{{Values: []interface{}{&git.Repository{}, nil}}, {Values: []interface{}{nil, fmt.Errorf("open error")}}}) defer patch.Reset() f := &api.UploadFileRequest{ Key: "test/upload_version.test1", Content: tmp2.content, ContentType: tmp2.contentType, - UseVersion: true, Sha256: tmp2.sha256, Bucket: bucket, } @@ -1343,9 +1351,19 @@ func testUploadVersionCommitErr(t *testing.T) { }) } +func testGetFileByID(t *testing.T){ + fileBiz:=newFileBiz() + c.Convey("test get file by id",t,func(){ + rsp,err:=fileBiz.GetFileByID(context.TODO(),fileId) + c.So(err,c.ShouldBeNil) + c.So(rsp,c.ShouldNotBeNil) + }) + +} func TestFile(t *testing.T) { t.Run("test make bucket", testMkBucket) t.Run("test upload", testPutFile) + t.Run("test get file by id",testGetFileByID) t.Run("test upload version commit error", testUploadVersionCommitErr) t.Run("test download", testDownload) t.Run("test initiate upload file", testInitiateUploadFile) diff --git a/internal/biz/file/minio.go b/internal/biz/file/minio.go index a64f99b..19e59e6 100644 --- a/internal/biz/file/minio.go +++ b/internal/biz/file/minio.go @@ -28,11 +28,17 @@ type MinioUseCase struct { func NewMinioUseCase(minioClient *minio.Client, localFile *FileUsecaseImpl) FileUsecase { return &MinioUseCase{minioClient: minioClient, localFile: localFile} } -func (m *MinioUseCase) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { +func (m *MinioUseCase) MakeBucket(ctx context.Context, in *api.MakeBucketRequest,_ string) (*api.MakeBucketResponse, error) { err := m.minioClient.MakeBucket(ctx, in.Bucket, minio.MakeBucketOptions{Region: in.Region, ObjectLocking: in.ObjectLocking}) if err != nil { - return nil, gosdk.NewError(fmt.Errorf("failed to make bucket: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket") + return nil, gosdk.NewError(fmt.Errorf("failed to make bucket: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket",gosdk.WithClientMessage(err.Error())) } + err = m.minioClient.EnableVersioning(ctx, in.Bucket) + if err!=nil{ + return nil, gosdk.NewError(fmt.Errorf("failed to enable versioning: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "enable_versioning",gosdk.WithClientMessage(err.Error())) + + } + return &api.MakeBucketResponse{}, nil } func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, authorId string) (*api.UploadFileResponse, error) { @@ -41,9 +47,9 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au return nil, gosdk.NewError(fmt.Errorf("failed to check bucket: %w or bucket %s not exist", err, in.Bucket), int32(common.Code_INTERNAL_ERROR), codes.Internal, "check_bucket") } contentLength := len(in.Content) - if in.UseVersion { - _ = m.minioClient.EnableVersioning(ctx, in.Bucket) - } + // if in.UseVersion { + // _ = m.minioClient.EnableVersioning(ctx, in.Bucket) + // } hash := sha256.New() hash.Write([]byte(in.Content)) checksum := hash.Sum(nil) @@ -87,7 +93,7 @@ func (m *MinioUseCase) AbortMultipartUpload(ctx context.Context, in *api.AbortMu return m.localFile.AbortMultipartUpload(ctx, in) } func (m *MinioUseCase) CompleteMultipartUploadFile(ctx context.Context, in *api.CompleteMultipartUploadRequest, authorId string) (*api.CompleteMultipartUploadResponse, error) { - _, _ = m.localFile.MakeBucket(ctx, &api.MakeBucketRequest{Bucket: in.Bucket}) + // _, _ = m.localFile.MakeBucket(ctx, &api.MakeBucketRequest{Bucket: in.Bucket},authorId) originKey := in.Key rsp, err := m.localFile.CompleteMultipartUploadFile(ctx, in, authorId) @@ -102,9 +108,9 @@ func (m *MinioUseCase) CompleteMultipartUploadFile(ctx context.Context, in *api. } defer file.Close() stat, _ := os.Stat(filePath) - if in.UseVersion { - _ = m.minioClient.EnableVersioning(ctx, in.Bucket) - } + // if in.UseVersion { + // _ = m.minioClient.EnableVersioning(ctx, in.Bucket) + // } userMetadata := map[string]string{ "x-amz-meta-sha256": in.Sha256, diff --git a/internal/biz/file/minio_test.go b/internal/biz/file/minio_test.go index 21f36a6..0e4b69d 100644 --- a/internal/biz/file/minio_test.go +++ b/internal/biz/file/minio_test.go @@ -51,7 +51,8 @@ func testMinioMkBucket(t *testing.T) { c.Convey("test make bucket success", t, func() { rsp, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ Bucket: minioBucket, - }) + EnableVersion: true, + },minioFileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) }) @@ -60,9 +61,21 @@ func testMinioMkBucket(t *testing.T) { defer patch.Reset() _, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ Bucket: "", - }) + },minioFileAuthor) + patch.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "mkdir error") + + patch2 := gomonkey.ApplyFuncReturn((*minio.Client).EnableVersioning, fmt.Errorf("enable error")) + defer patch2.Reset() + _, err = fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ + Bucket: fmt.Sprintf("bucket-minio-biz-4-%s", time.Now().Format("20060102150405")), + EnableVersion: true, + },minioFileAuthor) + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "enable error") + }) } @@ -74,7 +87,6 @@ func testMinioUpload(t *testing.T) { Bucket: minioBucket, Key: "test.txt", Content: []byte("hello"), - UseVersion: true, }, minioFileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) @@ -274,8 +286,8 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { UploadId: uploadId, Bucket: minioBucket, ContentType: "text/plain", - UseVersion: true, Sha256: minioBigFileSha256, + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) patchx.Reset() c.So(err, c.ShouldNotBeNil) @@ -287,8 +299,8 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { UploadId: uploadId, Bucket: "", ContentType: "text/plain", - UseVersion: true, Sha256: minioBigFileSha256, + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrBucketNotFound.Error()) @@ -300,7 +312,6 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { UploadId: minioUploadId, Bucket: minioBucket, ContentType: "text/plain", - UseVersion: true, Sha256: minioBigFileSha256, Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) diff --git a/internal/middleware/auth/ak_test.go b/internal/middleware/auth/ak_test.go index 789f7cb..9517232 100644 --- a/internal/middleware/auth/ak_test.go +++ b/internal/middleware/auth/ak_test.go @@ -97,16 +97,16 @@ func TestRequestBeforeErr(t *testing.T) { c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "NewGatewayRequestFromGrpc err") patch1 := gomonkey.ApplyFuncReturn((*biz.AccessKeyAuth).AppValidator, "dxadada", nil) - patch1 = patch1.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppid, "", fmt.Errorf("appid is empty")) + patch1 = patch1.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppOwner, "", fmt.Errorf("owner is empty")) defer patch1.Reset() _, err1 := ak.RequestBefore(context.TODO(), &grpc.UnaryServerInfo{}, nil) patch1.Reset() c.So(err1, c.ShouldNotBeNil) - c.So(err1.Error(), c.ShouldContainSubstring, "appid is empty") + c.So(err1.Error(), c.ShouldContainSubstring, "owner is empty") patch2 := gomonkey.ApplyFuncReturn(gosdk.NewGatewayRequestFromGrpc, nil, nil) patch2 = patch2.ApplyFuncReturn((*biz.AccessKeyAuth).AppValidator, "dxadada", nil) - patch2 = patch2.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppid, "dadad", nil) + patch2 = patch2.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppOwner, "dadad", nil) defer patch2.Reset() ctx, err2 := ak.RequestBefore(context.TODO(), &grpc.UnaryServerInfo{}, nil) patch2.Reset() diff --git a/internal/middleware/auth/aksk.go b/internal/middleware/auth/aksk.go index f2db94d..1612a4a 100644 --- a/internal/middleware/auth/aksk.go +++ b/internal/middleware/auth/aksk.go @@ -54,7 +54,7 @@ func (a *AccessKeyAuthMiddleware) RequestBefore(ctx context.Context, info *grpc. } - appid, err := a.app.GetAppid(ctx, accessKey) + owner, err := a.app.GetAppOwner(ctx, accessKey) if err != nil { return ctx, err @@ -63,14 +63,23 @@ func (a *AccessKeyAuthMiddleware) RequestBefore(ctx context.Context, info *grpc. if !ok { md = metadata.MD{} } - md.Set("x-identity", appid) + md.Set(gosdk.HeaderXIdentity, owner) ctx = metadata.NewIncomingContext(ctx, md) return ctx, nil } func (a *AccessKeyAuthMiddleware) ValidateStream(ctx context.Context, req interface{}, fullName string, headers Header) (context.Context, error) { - return a.RequestBefore(ctx, &grpc.UnaryServerInfo{FullMethod: fullName}, req) + ctx,err:= a.RequestBefore(ctx, &grpc.UnaryServerInfo{FullMethod: fullName}, req) + if err!=nil{ + return ctx,err + } + md, _ := metadata.FromIncomingContext(ctx) + if identity := md.Get(gosdk.HeaderXIdentity);len(identity)>0{ + headers.Set(strings.ToLower(gosdk.HeaderXIdentity), identity[0]) + } + return ctx,nil + } func (a *AccessKeyAuthMiddleware) StreamRequestBefore(ctx context.Context, ss grpc.ServerStream, info *grpc.StreamServerInfo, req interface{}) (grpc.ServerStream, error) { grpcStream := NewGrpcStream(ss, info.FullMethod, ss.Context(), a) diff --git a/internal/middleware/auth/apikey.go b/internal/middleware/auth/apikey.go index 4876803..0b9e9c9 100644 --- a/internal/middleware/auth/apikey.go +++ b/internal/middleware/auth/apikey.go @@ -2,7 +2,10 @@ package auth import ( "context" + "fmt" + "strings" + "github.com/begonia-org/begonia/internal/biz" "github.com/begonia-org/begonia/internal/pkg" "github.com/begonia-org/begonia/internal/pkg/config" gosdk "github.com/begonia-org/go-sdk" @@ -21,6 +24,7 @@ type ApiKeyAuthImpl struct { config *config.Config priority int name string + authz *biz.AuthzUsecase } func (a *ApiKeyAuthImpl) SetPriority(priority int) { @@ -37,41 +41,56 @@ func (a *ApiKeyAuthImpl) UnaryInterceptor(ctx context.Context, req any, info *gr return handler(ctx, req) } - if err := a.check(ctx); err != nil { - return nil, err + apikey := "" + if apikey, err = a.check(ctx); err == nil && apikey != "" { + identity, err := a.authz.GetIdentity(ctx, gosdk.ApiKeyType, apikey) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("query uid base on apikey get error:%w", err), int32(api.UserSvrCode_USER_APIKEY_NOT_MATCH_ERR), codes.Unauthenticated, "authorization_check") + } + ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXIdentity, identity)) + return handler(ctx, req) } - return handler(ctx, req) + return nil, err } -func NewApiKeyAuth(config *config.Config) ApiKeyAuth { +func NewApiKeyAuth(config *config.Config, authz *biz.AuthzUsecase) ApiKeyAuth { return &ApiKeyAuthImpl{ config: config, + authz: authz, name: "api_key_auth", } } -func (a *ApiKeyAuthImpl) check(ctx context.Context) error { +func (a *ApiKeyAuthImpl) check(ctx context.Context) (string, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { - return gosdk.NewError(status.Errorf(codes.Unauthenticated, "metadata not exists in context"), int32(api.UserSvrCode_USER_AUTH_MISSING_ERR), codes.Unauthenticated, "authorization_check") + return "", gosdk.NewError(status.Errorf(codes.Unauthenticated, "metadata not exists in context"), int32(api.UserSvrCode_USER_AUTH_MISSING_ERR), codes.Unauthenticated, "authorization_check") } // authorization := a.GetAuthorizationFromMetadata(md) - apikeys := md.Get("x-api-key") + apikeys := md.Get(gosdk.HeaderXApiKey) if len(apikeys) == 0 { - return gosdk.NewError(status.Errorf(codes.Unauthenticated, "apikey not exists in context"), int32(api.UserSvrCode_USER_AUTH_MISSING_ERR), codes.Unauthenticated, "authorization_check") + return "", gosdk.NewError(status.Errorf(codes.Unauthenticated, "apikey not exists in context"), int32(api.UserSvrCode_USER_AUTH_MISSING_ERR), codes.Unauthenticated, "authorization_check") } apikey := apikeys[0] if apikey != a.config.GetAdminAPIKey() { - return gosdk.NewError(pkg.ErrAPIKeyNotMatch, int32(api.UserSvrCode_USER_APIKEY_NOT_MATCH_ERR), codes.Unauthenticated, "authorization_check") + return "", gosdk.NewError(pkg.ErrAPIKeyNotMatch, int32(api.UserSvrCode_USER_APIKEY_NOT_MATCH_ERR), codes.Unauthenticated, "authorization_check") } - return nil + return apikey, nil } func (a *ApiKeyAuthImpl) ValidateStream(ctx context.Context, req interface{}, fullName string, headers Header) (context.Context, error) { - if err := a.check(ctx); err != nil { + apikey := "" + var err error + if apikey, err = a.check(ctx); err == nil && apikey != "" { + identity, err := a.authz.GetIdentity(ctx, gosdk.ApiKeyType, apikey) + if err != nil { + return ctx, gosdk.NewError(fmt.Errorf("query user id base on apikey err:%w", err), int32(api.UserSvrCode_USER_APIKEY_NOT_MATCH_ERR), codes.Unauthenticated, "authorization_check") + } + ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXIdentity, identity)) + headers.Set(strings.ToLower(gosdk.HeaderXIdentity), identity) return ctx, err } - return ctx, nil + return ctx, err } func (a *ApiKeyAuthImpl) StreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { diff --git a/internal/middleware/auth/apikey_test.go b/internal/middleware/auth/apikey_test.go index cff9530..46312ff 100644 --- a/internal/middleware/auth/apikey_test.go +++ b/internal/middleware/auth/apikey_test.go @@ -7,13 +7,18 @@ import ( "runtime" "testing" + "github.com/agiledragon/gomonkey/v2" "github.com/begonia-org/begonia" "github.com/begonia-org/begonia/config" "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/data" "github.com/begonia-org/begonia/internal/middleware/auth" "github.com/begonia-org/begonia/internal/pkg" cfg "github.com/begonia-org/begonia/internal/pkg/config" + "github.com/begonia-org/begonia/internal/pkg/crypto" "github.com/begonia-org/begonia/internal/pkg/routers" + gosdk "github.com/begonia-org/go-sdk" hello "github.com/begonia-org/go-sdk/api/example/v1" c "github.com/smartystreets/goconvey/convey" "google.golang.org/grpc" @@ -28,7 +33,8 @@ func TestAPIKeyUnaryInterceptor(t *testing.T) { } config := config.ReadConfig(env) cnf := cfg.NewConfig(config) - apikey := auth.NewApiKeyAuth(cnf) + authz := newAuthzBiz() + apikey := auth.NewApiKeyAuth(cnf, authz) apikey.SetPriority(1) c.So(apikey.Name(), c.ShouldEqual, "api_key_auth") c.So(apikey.Priority(), c.ShouldEqual, 1) @@ -64,7 +70,7 @@ func TestAPIKeyUnaryInterceptor(t *testing.T) { }) c.So(err.Error(), c.ShouldContainSubstring, fmt.Errorf("apikey not exists in context").Error()) - _, err = apikey.UnaryInterceptor(metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-api-key", "test")), &hello.HelloRequest{}, &grpc.UnaryServerInfo{ + _, err = apikey.UnaryInterceptor(metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, "test")), &hello.HelloRequest{}, &grpc.UnaryServerInfo{ FullMethod: "/integration.TestService/Get", }, func(ctx context.Context, req interface{}) (interface{}, error) { return nil, nil @@ -72,16 +78,45 @@ func TestAPIKeyUnaryInterceptor(t *testing.T) { }) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrAPIKeyNotMatch.Error()) - _, err = apikey.UnaryInterceptor(metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-api-key", cnf.GetAdminAPIKey())), &hello.HelloRequest{}, &grpc.UnaryServerInfo{ + _, err = apikey.UnaryInterceptor(metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())), &hello.HelloRequest{}, &grpc.UnaryServerInfo{ FullMethod: "/integration.TestService/Get", }, func(ctx context.Context, req interface{}) (interface{}, error) { - return nil, nil + md, _ := metadata.FromIncomingContext(ctx) + if len(md.Get(gosdk.HeaderXIdentity)) == 0 || md.Get(gosdk.HeaderXIdentity)[0] == "" { + t.Error("identity not exists in context") + return nil, fmt.Errorf("identity not exists in context") + } + return nil, nil }) c.So(err, c.ShouldBeNil) + + patch := gomonkey.ApplyFuncReturn((*biz.AuthzUsecase).GetIdentity, "", fmt.Errorf("get user id base on apikey error")) + defer patch.Reset() + _, err = apikey.UnaryInterceptor(metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())), &hello.HelloRequest{}, &grpc.UnaryServerInfo{ + FullMethod: "/integration.TestService/Get", + }, func(ctx context.Context, req interface{}) (interface{}, error) { + return nil, nil + }) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get user id base on apikey error") }) } - +func newAuthzBiz() *biz.AuthzUsecase { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + config := config.ReadConfig(env) + repo := data.NewAuthzRepo(config, gateway.Log) + user := data.NewUserRepo(config, gateway.Log) + cnf := cfg.NewConfig(config) + crypto := crypto.NewUsersAuth(cnf) + app := data.NewAppRepo(config, gateway.Log) + + return biz.NewAuthzUsecase(repo, user, app, gateway.Log, crypto, cnf) +} func TestApiKeyStreamInterceptor(t *testing.T) { c.Convey("TestApiKeyStreamInterceptor", t, func() { env := "dev" @@ -90,7 +125,8 @@ func TestApiKeyStreamInterceptor(t *testing.T) { } config := config.ReadConfig(env) cnf := cfg.NewConfig(config) - apikey := auth.NewApiKeyAuth(cnf) + authz := newAuthzBiz() + apikey := auth.NewApiKeyAuth(cnf, authz) apikey.SetPriority(1) c.So(apikey.Name(), c.ShouldEqual, "api_key_auth") c.So(apikey.Priority(), c.ShouldEqual, 1) @@ -109,10 +145,36 @@ func TestApiKeyStreamInterceptor(t *testing.T) { c.So(err, c.ShouldBeNil) err = apikey.StreamInterceptor(&hello.HelloRequest{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ - ctx: metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-api-key", cnf.GetAdminAPIKey())), + ctx: metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())), }}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { return ss.RecvMsg(srv) }) c.So(err, c.ShouldBeNil) + + err = apikey.StreamInterceptor(&hello.HelloRequest{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ + ctx: metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())), + }}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { + + err:=ss.RecvMsg(srv) + md, _ := metadata.FromIncomingContext(ss.Context()) + if len(md.Get(gosdk.HeaderXIdentity)) == 0 || md.Get(gosdk.HeaderXIdentity)[0] == "" { + t.Error("identity not exists in context") + return fmt.Errorf("identity not exists in context") + } + return err + + }) + c.So(err, c.ShouldBeNil) + + patch := gomonkey.ApplyFuncReturn((*biz.AuthzUsecase).GetIdentity, "", fmt.Errorf("get user id base on apikey error")) + defer patch.Reset() + err = apikey.StreamInterceptor(&hello.HelloRequest{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ + ctx: metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())), + }}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { + return ss.RecvMsg(srv) + }) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get user id base on apikey error") }) } diff --git a/internal/middleware/auth/auth.go b/internal/middleware/auth/auth.go index d34ad0d..acc21cd 100644 --- a/internal/middleware/auth/auth.go +++ b/internal/middleware/auth/auth.go @@ -38,7 +38,7 @@ func (a *Auth) UnaryInterceptor(ctx context.Context, req any, info *grpc.UnarySe if !ok { return nil, status.Errorf(codes.Unauthenticated, "metadata not exists in context") } - xApiKey := md.Get("x-api-key") + xApiKey := md.Get(gosdk.HeaderXApiKey) if len(xApiKey) != 0 { return a.apikey.UnaryInterceptor(ctx, req, info, handler) } diff --git a/internal/middleware/auth/auth_test.go b/internal/middleware/auth/auth_test.go index e122ef4..78727db 100644 --- a/internal/middleware/auth/auth_test.go +++ b/internal/middleware/auth/auth_test.go @@ -120,7 +120,8 @@ func getJWT() string { user := data.NewUserRepo(config, gateway.Log) userAuth := crypto.NewUsersAuth(cnf) authzRepo := data.NewAuthzRepo(config, gateway.Log) - authz := biz.NewAuthzUsecase(authzRepo, user, gateway.Log, userAuth, cnf) + appRepo:=data.NewAppRepo(config,gateway.Log) + authz := biz.NewAuthzUsecase(authzRepo, user,appRepo, gateway.Log, userAuth, cnf) adminUser := cnf.GetDefaultAdminName() adminPasswd := cnf.GetDefaultAdminPasswd() _, filename, _, _ := runtime.Caller(0) @@ -212,10 +213,12 @@ func getMid() gosdk.LocalPlugin { user := data.NewUserRepo(config, gateway.Log) userAuth := crypto.NewUsersAuth(cnf) authzRepo := data.NewAuthzRepo(config, gateway.Log) - authz := biz.NewAuthzUsecase(authzRepo, user, gateway.Log, userAuth, cnf) + appRepo:=data.NewAppRepo(config,gateway.Log) + authz := biz.NewAuthzUsecase(authzRepo, user,appRepo, gateway.Log, userAuth, cnf) jwt := auth.NewJWTAuth(cnf, tiga.NewRedisDao(config), authz, gateway.Log) ak := auth.NewAccessKeyAuth(akBiz, cnf, gateway.Log) - apiKey := auth.NewApiKeyAuth(cnf) + + apiKey := auth.NewApiKeyAuth(cnf,authz) mid := auth.NewAuth(ak, jwt, apiKey) return mid @@ -231,6 +234,13 @@ func TestUnaryInterceptor(t *testing.T) { mid := getMid() ctx := context.Background() handler := func(ctx context.Context, req interface{}) (interface{}, error) { + md,ok:=metadata.FromIncomingContext(ctx) + if !ok{ + return nil,fmt.Errorf("no metadata") + } + if identify:=md.Get(gosdk.HeaderXIdentity);len(identify)==0||identify[0]==""{ + return nil,fmt.Errorf("no app identity") + } return nil, nil } R := routers.Get() @@ -241,11 +251,11 @@ func TestUnaryInterceptor(t *testing.T) { R.LoadAllRouters(pd) c.Convey("TestUnaryInterceptor api-key", t, func() { - ctx = metadata.NewIncomingContext(ctx, metadata.Pairs("x-api-key", cnf.GetAdminAPIKey())) + ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())) _, err := mid.UnaryInterceptor(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, handler) c.So(err, c.ShouldBeNil) - ctx = metadata.NewIncomingContext(ctx, metadata.Pairs("x-api-key", "123")) + ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXApiKey, "123")) _, err = mid.UnaryInterceptor(ctx, nil, &grpc.UnaryServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, handler) c.So(err, c.ShouldNotBeNil) mid.SetPriority(1) @@ -285,7 +295,7 @@ func TestUnaryInterceptor(t *testing.T) { md.Append("uri", "/test/post") md.Append("x-http-method", http.MethodPost) patch := gomonkey.ApplyFuncReturn((*biz.AccessKeyAuth).GetSecret, secret, nil) - patch = patch.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppid, appid, nil) + patch = patch.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppOwner, appid, nil) defer patch.Reset() ctx := metadata.NewIncomingContext(context.Background(), md) _, err = mid.UnaryInterceptor(ctx, u, &grpc.UnaryServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, handler) @@ -330,12 +340,12 @@ func TestStreamInterceptor(t *testing.T) { R.LoadAllRouters(pd) c.Convey("TestStreamInterceptor apikey", t, func() { - ctx = metadata.NewIncomingContext(ctx, metadata.Pairs("x-api-key", cnf.GetAdminAPIKey())) + ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())) err = mid.StreamInterceptor(&v1.Users{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ctx: ctx}}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { return nil }) c.So(err, c.ShouldBeNil) - ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-api-key", "cnf.GetAdminAPIKey()")) + ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, "cnf.GetAdminAPIKey()")) err = mid.StreamInterceptor(&v1.Users{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ctx: ctx}}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { return ss.RecvMsg(srv) }) @@ -377,11 +387,19 @@ func TestStreamInterceptor(t *testing.T) { md.Append("uri", "/test/post") md.Append("x-http-method", http.MethodPost) patch := gomonkey.ApplyFuncReturn((*biz.AccessKeyAuth).GetSecret, secret, nil) - patch = patch.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppid, appid, nil) + patch = patch.ApplyFuncReturn((*biz.AccessKeyAuth).GetAppOwner, appid, nil) defer patch.Reset() ctx := metadata.NewIncomingContext(context.Background(), md) err = mid.StreamInterceptor(u, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ctx: ctx}}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/POST"}, func(srv interface{}, ss grpc.ServerStream) error { - return ss.RecvMsg(srv) + err:= ss.RecvMsg(srv) + md,ok:=metadata.FromIncomingContext(ss.Context()) + if !ok{ + return fmt.Errorf("no metadata") + } + if identify:=md.Get(gosdk.HeaderXIdentity);len(identify)==0||identify[0]==""{ + return fmt.Errorf("no app identity") + } + return err }) c.So(err, c.ShouldBeNil) diff --git a/internal/middleware/auth/jwt.go b/internal/middleware/auth/jwt.go index 83a8f5c..a90757c 100644 --- a/internal/middleware/auth/jwt.go +++ b/internal/middleware/auth/jwt.go @@ -163,6 +163,7 @@ func (a *JWTAuth) checkJWT(ctx context.Context, authorization string, rspHeader // 设置uid reqHeader.Set("x-token", token) reqHeader.Set("x-uid", payload.Uid) + reqHeader.Set(gosdk.HeaderXIdentity, payload.Uid) return true, nil } diff --git a/internal/middleware/auth/jwt_test.go b/internal/middleware/auth/jwt_test.go index 75f5cf4..295030d 100644 --- a/internal/middleware/auth/jwt_test.go +++ b/internal/middleware/auth/jwt_test.go @@ -71,7 +71,8 @@ func TestJWTUnaryInterceptor(t *testing.T) { user := data.NewUserRepo(config, gateway.Log) userAuth := crypto.NewUsersAuth(cnf) authzRepo := data.NewAuthzRepo(config, gateway.Log) - authz := biz.NewAuthzUsecase(authzRepo, user, gateway.Log, userAuth, cnf) + appRepo:=data.NewAppRepo(config,gateway.Log) + authz := biz.NewAuthzUsecase(authzRepo, user,appRepo, gateway.Log, userAuth, cnf) jwt := auth.NewJWTAuth(cnf, tiga.NewRedisDao(config), authz, gateway.Log) jwt.SetPriority(1) c.So(jwt.Priority(), c.ShouldEqual, 1) @@ -297,7 +298,8 @@ func TestJWTStreamInterceptor(t *testing.T) { user := data.NewUserRepo(config, gateway.Log) userAuth := crypto.NewUsersAuth(cnf) authzRepo := data.NewAuthzRepo(config, gateway.Log) - authz := biz.NewAuthzUsecase(authzRepo, user, gateway.Log, userAuth, cnf) + appRepo:=data.NewAppRepo(config,gateway.Log) + authz := biz.NewAuthzUsecase(authzRepo, user,appRepo, gateway.Log, userAuth, cnf) jwt := auth.NewJWTAuth(cnf, tiga.NewRedisDao(config), authz, gateway.Log) err := jwt.StreamInterceptor(&hello.HelloRequest{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ ctx: metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-api-key", cnf.GetAdminAPIKey())), diff --git a/internal/middleware/http.go b/internal/middleware/http.go index 4fe8e85..c87c4d7 100644 --- a/internal/middleware/http.go +++ b/internal/middleware/http.go @@ -3,6 +3,7 @@ package middleware import ( "context" "fmt" + "log" "strings" "github.com/begonia-org/begonia/internal/pkg/routers" @@ -57,7 +58,7 @@ func (s *HttpStream) SendMsg(m interface{}) error { router := routersList.GetRouteByGrpcMethod(s.FullMethod) // 对内置服务的http响应进行格式化 if routersList.IsLocalSrv(s.FullMethod) || router.UseJsonResponse { - + log.Printf("转换fullMethod:%v", s.FullMethod) rsp, _ := grpcToHttpResponse(m, nil) return s.ServerStream.SendMsg(rsp) } @@ -149,7 +150,6 @@ func grpcToHttpResponse(rsp interface{}, err error) (*common.HttpResponse, error return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "internal_error") } } - return &common.HttpResponse{ Code: int32(common.Code_OK), Message: "success", diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go index e19c43e..957c6dc 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/middleware.go @@ -31,7 +31,7 @@ func New(config *config.Config, ) *PluginsApply { jwt := auth.NewJWTAuth(config, rdb, user, log) ak := auth.NewAccessKeyAuth(authz, config, log) - apiKey := auth.NewApiKeyAuth(config) + apiKey := auth.NewApiKeyAuth(config,user) plugins := map[string]gosdk.LocalPlugin{ "onlyJWT": jwt, "onlyAK": ak, diff --git a/internal/middleware/middleware_test.go b/internal/middleware/middleware_test.go index be8a8e6..e6ea5a1 100644 --- a/internal/middleware/middleware_test.go +++ b/internal/middleware/middleware_test.go @@ -28,7 +28,8 @@ func TestMiddlewareUnaryInterceptorChains(t *testing.T) { user := data.NewUserRepo(config, gateway.Log) userAuth := crypto.NewUsersAuth(cnf) authzRepo := data.NewAuthzRepo(config, gateway.Log) - authz := biz.NewAuthzUsecase(authzRepo, user, gateway.Log, userAuth, cnf) + appRepo:=data.NewAppRepo(config,gateway.Log) + authz := biz.NewAuthzUsecase(authzRepo, user,appRepo, gateway.Log, userAuth, cnf) repo := data.NewAppRepo(config, gateway.Log) akBiz := biz.NewAccessKeyAuth(repo, cnf, gateway.Log) diff --git a/internal/migrate/migrate.go b/internal/migrate/migrate.go index 9978c9b..84b5bca 100644 --- a/internal/migrate/migrate.go +++ b/internal/migrate/migrate.go @@ -35,7 +35,6 @@ func NewMySQLMigrate(mysql *tiga.MySQLDao, models ...TableModel) *MySQLMigrate { } func (m *MySQLMigrate) Do() error { - for _, model := range m.models { err := m.mysql.AutoMigrate(model) if err != nil { diff --git a/internal/service/file.go b/internal/service/file.go index 0bf138c..652b66f 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -3,6 +3,7 @@ package service import ( "context" "fmt" + "log" "net/http" "net/url" "strconv" @@ -73,7 +74,7 @@ func (f *FileService) Upload(ctx context.Context, in *api.UploadFileRequest) (*a if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - + log.Printf("upload file author:%v", identity) rsp, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { return f.biz[in.GetEngine()].Upload(ctx, req.(*api.UploadFileRequest), identity) }) @@ -124,6 +125,7 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } + log.Printf("download file author:%v", identity) newKey, err := url.PathUnescape(in.Key) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "url_unescape") @@ -138,22 +140,10 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h // return r,err buf, _ := r.([]byte) - // shaer := sha256.New() - // shaer.Write(buf) _, err = f.Metadata(ctx, &api.FileMetadataRequest{Bucket: in.Bucket, Engine: in.Engine, Key: in.Key, Version: in.Version}) if err != nil { return nil, err } - // log.Printf("bucket,%s", meta.Bucket) - // rspMd := metadata.Pairs( - // gosdk.GetMetadataKey("Content-Length"), fmt.Sprintf("%d", len(buf)), - // gosdk.GetMetadataKey("X-File-Sha256"), hex.EncodeToString(shaer.Sum(nil)), - // gosdk.GetMetadataKey("Content-Type"), meta.ContentType, - // gosdk.GetMetadataKey("X-File-Version"), meta.Version, - // gosdk.GetMetadataKey("X-File-Name"), meta.Key, - // gosdk.GetMetadataKey("X-File-Bucket"), meta.Bucket, - // ) - // _ = grpc.SendHeader(ctx, rspMd) rsp := &httpbody.HttpBody{ ContentType: http.DetectContentType(buf), @@ -332,10 +322,15 @@ func (f *FileService) ListFiles(ctx context.Context, in *api.ListFilesRequest) ( return &api.ListFilesResponse{Files: files}, err } func (f *FileService) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { + identity := "" + if identity = GetIdentity(ctx); identity == "" { + return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") + } r, err := f.serviceDecorator(ctx, in, func(req request) (interface{}, error) { - return f.biz[in.Engine].MakeBucket(ctx, req.(*api.MakeBucketRequest)) + return f.biz[in.Engine].MakeBucket(ctx, req.(*api.MakeBucketRequest), identity) }) rsp, _ := r.(*api.MakeBucketResponse) + // log.Printf("make bucket:%v", err) return rsp, err } func (f *FileService) Desc() *grpc.ServiceDesc { diff --git a/internal/service/file_test.go b/internal/service/file_test.go index 7a45b26..3ad7ae9 100644 --- a/internal/service/file_test.go +++ b/internal/service/file_test.go @@ -63,14 +63,25 @@ func makeBucket(t *testing.T) { c.Convey("test make bucket", t, func() { fileBucket = fmt.Sprintf("test-service-bucket-%s", time.Now().Format("20060102150405")) apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) - rsp, err := apiClient.CreateBucket(context.Background(), fileBucket, "test", false) + rsp, err := apiClient.CreateBucket(context.Background(), fileBucket, "test", false,true) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) minioFile := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_MINIO) - rsp, err = minioFile.CreateBucket(context.Background(), fileBucket, "test", false) + rsp, err = minioFile.CreateBucket(context.Background(), fileBucket, "test", false,true) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + + // no idend + patch := gomonkey.ApplyFuncReturn(service.GetIdentity, "") + defer patch.Reset() + + // apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) + _, err = apiClient.CreateBucket(context.Background(), fileBucket, "test", false,true) + c.So(err, c.ShouldNotBeNil) + patch.Reset() + + }) } func upload(t *testing.T) { @@ -206,7 +217,7 @@ func download(t *testing.T) { c.So(err, c.ShouldBeNil) defer tmp.Close() defer os.Remove(tmp.Name()) - sha256Str, err := apiClient.DownloadFile(context.Background(), sdkAPPID+"/test/helloworld.pb", tmp.Name(), "", fileBucket) + sha256Str, err := apiClient.DownloadFile(context.Background(), "test/helloworld.pb", tmp.Name(), "", fileBucket) c.So(err, c.ShouldBeNil) _, err = os.Stat(tmp.Name()) c.So(err, c.ShouldBeNil) @@ -218,7 +229,7 @@ func download(t *testing.T) { patch2 := gomonkey.ApplyFuncReturn((*service.FileService).Metadata, nil, fmt.Errorf("test metadata error")) defer patch2.Reset() - _, err = apiClient.DownloadFile(context.Background(), sdkAPPID+"/test/helloworld.pb", tmp.Name(), "", fileBucket) + _, err = apiClient.DownloadFile(context.Background(), "test/helloworld.pb", tmp.Name(), "", fileBucket) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "unknown error") patch2.Reset() @@ -243,7 +254,7 @@ func downloadParts(t *testing.T) { c.So(err, c.ShouldBeNil) defer tmp.Close() defer os.Remove(tmp.Name()) - rsp, err := apiClient.DownloadMultiParts(context.Background(), sdkAPPID+"/test/tmp.bin", tmp.Name(), "", fileBucket) + rsp, err := apiClient.DownloadMultiParts(context.Background(), "test/tmp.bin", tmp.Name(), "", fileBucket) c.So(err, c.ShouldBeNil) // c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) downloadedSha256, _ := sumFileSha256(tmp.Name()) @@ -259,7 +270,7 @@ func deleteFile(t *testing.T) { } c.Convey("test delete file", t, func() { apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) - rsp, err := apiClient.DeleteFile(context.Background(), sdkAPPID+"/test/helloworld.pb", fileBucket) + rsp, err := apiClient.DeleteFile(context.Background(), "test/helloworld.pb", fileBucket) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) conf := cfg.NewConfig(config.ReadConfig(env)) @@ -281,17 +292,17 @@ func testRangeDownload(t *testing.T) { c.So(err, c.ShouldBeNil) defer tmp.Close() defer os.Remove(tmp.Name()) - rsp, err := apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", -1, 128, fileBucket) + rsp, err := apiClient.RangeDownload(context.Background(), "test/tmp.bin", "", -1, 128, fileBucket) c.So(err, c.ShouldBeNil) c.So(len(rsp), c.ShouldEqual, 129) - rsp, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1, fileBucket) + rsp, err = apiClient.RangeDownload(context.Background(), "test/tmp.bin", "", 128, -1, fileBucket) c.So(err, c.ShouldBeNil) c.So(len(rsp), c.ShouldEqual, 1024*1024*2-128) patch := gomonkey.ApplyFuncReturn(service.GetIdentity, "") defer patch.Reset() - _, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1, fileBucket) + _, err = apiClient.RangeDownload(context.Background(), "test/tmp.bin", "", 128, -1, fileBucket) c.So(err, c.ShouldNotBeNil) patch.Reset() diff --git a/internal/service/service.go b/internal/service/service.go index 42223d7..b3effcc 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -3,6 +3,7 @@ package service import ( "context" + gosdk "github.com/begonia-org/go-sdk" app "github.com/begonia-org/go-sdk/api/app/v1" ep "github.com/begonia-org/go-sdk/api/endpoint/v1" file "github.com/begonia-org/go-sdk/api/file/v1" @@ -45,7 +46,7 @@ func GetIdentity(ctx context.Context) string { if !ok { return "" } - identity := md.Get("x-identity") + identity := md.Get(gosdk.HeaderXIdentity) if len(identity) > 0 { return identity[0] } diff --git a/internal/service/wire_gen.go b/internal/service/wire_gen.go index a269815..c703403 100644 --- a/internal/service/wire_gen.go +++ b/internal/service/wire_gen.go @@ -34,8 +34,9 @@ func NewAuthzSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1.AuthS dataData := data.NewData(mySQLDao, redisDao, etcdDao) curd := data.NewCurdImpl(mySQLDao, configConfig) userRepo := data.NewUserRepoImpl(dataData, layeredCache, curd, configConfig) + appRepo := data.NewAppRepoImpl(curd, layeredCache, configConfig) usersAuth := crypto.NewUsersAuth(configConfig) - authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) + authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, appRepo, log, usersAuth, configConfig) authServiceServer := NewAuthzService(authzUsecase, log, usersAuth, configConfig) return authServiceServer } diff --git a/internal/wire_gen.go b/internal/wire_gen.go index 0b4bee0..21fe672 100644 --- a/internal/wire_gen.go +++ b/internal/wire_gen.go @@ -61,7 +61,7 @@ func New(config2 *tiga.Configuration, log logger.Logger, endpoint2 string) Gatew v := file.NewFileUsecase(configConfig, fileRepo) fileServiceServer := service.NewFileService(v, configConfig) usersAuth := crypto.NewUsersAuth(configConfig) - authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) + authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, appRepo, log, usersAuth, configConfig) authServiceServer := service.NewAuthzService(authzUsecase, log, usersAuth, configConfig) endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, configConfig) endpointServiceServer := service.NewEndpointsService(endpointUsecase, log, configConfig) @@ -88,8 +88,9 @@ func NewAuthzSvr(config2 *tiga.Configuration, log logger.Logger) v1.AuthServiceS dataData := data.NewData(mySQLDao, redisDao, etcdDao) curd := data.NewCurdImpl(mySQLDao, configConfig) userRepo := data.NewUserRepoImpl(dataData, layeredCache, curd, configConfig) + appRepo := data.NewAppRepoImpl(curd, layeredCache, configConfig) usersAuth := crypto.NewUsersAuth(configConfig) - authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) + authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, appRepo, log, usersAuth, configConfig) authServiceServer := service.NewAuthzService(authzUsecase, log, usersAuth, configConfig) return authServiceServer } @@ -157,7 +158,7 @@ func NewWorker(config2 *tiga.Configuration, log logger.Logger, gw string) Gatewa v := file.NewFileUsecase(configConfig, fileRepo) fileServiceServer := service.NewFileService(v, configConfig) usersAuth := crypto.NewUsersAuth(configConfig) - authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) + authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, appRepo, log, usersAuth, configConfig) authServiceServer := service.NewAuthzService(authzUsecase, log, usersAuth, configConfig) endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, configConfig) endpointServiceServer := service.NewEndpointsService(endpointUsecase, log, configConfig) From 446bdd8ea3dcc75fc2b3415f8b9e1602847de97c Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Mon, 17 Jun 2024 18:44:57 +0800 Subject: [PATCH 12/16] feat:tranform all auth key to uid --- go.sum | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/go.sum b/go.sum index c37dcda..c344733 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoa github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -23,10 +22,6 @@ github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa h1:DH github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa/go.mod h1:xEqoca1vNGqH8CV7X9EzhDV5Ihtq9J95p7ZipzUB6pc= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw7T+90KC5QW7yCa8Nn/5psnlvsexipAOrQAcc7YE0= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= -github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12 h1:4mZUWCrcl9gIoZaTUeQ9Qrty09WrK+eu2U5aipCy0Cw= -github.com/begonia-org/go-sdk v0.0.0-20240616174128-1fcd986dad12/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= -github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38 h1:jY6dVk3lzs8gHQBqLfqdZvz8L5u/MwGx9baTJK8Fj7Y= -github.com/begonia-org/go-sdk v0.0.0-20240617021643-1b9d4899ab38/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f h1:1roWosaPjA7vE5CnnF6irDWt05SLD57bsHLun4HYeLU= github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= @@ -123,7 +118,6 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -167,7 +161,6 @@ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -209,7 +202,6 @@ github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -237,8 +229,6 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643 h1:hFu6LvrCrPBpPU8JbgsxKPSQK+M6A5O2TzO5o7w9c70= -github.com/spark-lence/tiga v0.0.0-20240616174249-efb5797d3643/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4 h1:HmuGbn+mhKpi5tM8iM5vpUUx4K+XZZdrPecNu8IeO8c= github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -416,7 +406,6 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= From 1ccb00215ca12321be8dcbb27ba5ce9078e1ccd6 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Tue, 18 Jun 2024 00:19:38 +0800 Subject: [PATCH 13/16] feat:add auto create database --- cmd/begonia/endpoint.go | 17 +++++++------ cmd/begonia/main.go | 11 +++++--- config/settings.yml | 9 ++++++- gateway/endpoint_test.go | 7 +----- go.mod | 2 +- go.sum | 7 ++++++ internal/middleware/auth/aksk.go | 7 +++++- internal/middleware/auth/apikey.go | 11 +++++--- internal/middleware/auth/auth_test.go | 36 ++++++++++++++++++++++++--- internal/middleware/auth/headers.go | 5 ++-- internal/middleware/auth/jwt.go | 3 +-- internal/middleware/http.go | 2 -- internal/migrate/app.go | 8 +++--- internal/migrate/operator.go | 3 ++- internal/migrate/operator_test.go | 4 +-- internal/service/base_test.go | 3 ++- 16 files changed, 94 insertions(+), 41 deletions(-) diff --git a/cmd/begonia/endpoint.go b/cmd/begonia/endpoint.go index f8764c0..45ae1bb 100644 --- a/cmd/begonia/endpoint.go +++ b/cmd/begonia/endpoint.go @@ -18,14 +18,15 @@ var accessKey string var secret string var addr string -func readInitAPP() { +func readInitAPP(env string) { homeDir, err := os.UserHomeDir() if err != nil { log.Fatalf(err.Error()) return } + path := filepath.Join(homeDir, ".begonia") - path = filepath.Join(path, "admin-app.json") + path = filepath.Join(path, fmt.Sprintf("admin-app.%s.json", env)) file, err := os.Open(path) if err != nil { log.Fatalf(err.Error()) @@ -66,8 +67,8 @@ func readInitAPP() { addr = gw.Addr } -func RegisterEndpoint(name string, endpoints []string, pbFile string, opts ...client.EndpointOption) { - readInitAPP() +func RegisterEndpoint(env,name string, endpoints []string, pbFile string, opts ...client.EndpointOption) { + readInitAPP(env) pb, err := os.ReadFile(pbFile) if err != nil { panic(err) @@ -102,8 +103,8 @@ func RegisterEndpoint(name string, endpoints []string, pbFile string, opts ...cl log.Printf("#####################Add Endpoint Success#####################") log.Printf("#####################ID:%s####################################", rsp.Id) } -func UpdateEndpoint(id string, mask []string, opts ...client.EndpointOption) { - readInitAPP() +func UpdateEndpoint(env,id string, mask []string, opts ...client.EndpointOption) { + readInitAPP(env) apiClient := client.NewEndpointAPI(addr, accessKey, secret) log.Printf("#####################Update Endpoint###########################") patch := &endpoint.EndpointSrvUpdateRequest{} @@ -119,8 +120,8 @@ func UpdateEndpoint(id string, mask []string, opts ...client.EndpointOption) { log.Printf("#####################Update Endpoint %s Success#####################", id) } -func DeleteEndpoint(id string) { - readInitAPP() +func DeleteEndpoint(env,id string) { + readInitAPP(env) apiClient := client.NewEndpointAPI(addr, accessKey, secret) log.Printf("#####################Delete Endpoint:%s#####################", id) _, err := apiClient.DeleteEndpointConfig(context.Background(), id) diff --git a/cmd/begonia/main.go b/cmd/begonia/main.go index 2305c28..8bfbf93 100644 --- a/cmd/begonia/main.go +++ b/cmd/begonia/main.go @@ -75,8 +75,9 @@ func NewEndpointDelCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { id, _ := cmd.Flags().GetString("id") + env, _ := cmd.Flags().GetString("env") - DeleteEndpoint(id) + DeleteEndpoint(env,id) }, } cmd.Flags().StringP("id", "i", "", "ID Of Your Service") @@ -95,8 +96,9 @@ func NewEndpointAddCmd() *cobra.Command { tags, _ := cmd.Flags().GetStringArray("tags") balance, _ := cmd.Flags().GetString("balance") endpoints, _ := cmd.Flags().GetStringArray("endpoint") + env,_:=cmd.Flags().GetString("env") - RegisterEndpoint(name, endpoints, desc, client.WithBalance(strings.ToUpper(balance)), client.WithTags(tags)) + RegisterEndpoint(env,name, endpoints, desc, client.WithBalance(strings.ToUpper(balance)), client.WithTags(tags)) }, } cmd = newWriteEndpointCmd(cmd) @@ -156,7 +158,8 @@ func NewEndpointUpdateCmd() *cobra.Command { options = append(options, client.WithEndpoints(meta)) mask = append(mask, "endpoints") } - UpdateEndpoint(id, mask, options...) + env,_:=cmd.Flags().GetString("env") + UpdateEndpoint(env,id, mask, options...) }, } cmd = newWriteEndpointCmd(cmd) @@ -198,7 +201,7 @@ func main() { rootCmd.AddCommand(cmd) rootCmd.AddCommand(NewBegoniaInfoCmd()) rootCmd.AddCommand(addCommonCommand(NewInitCmd())) - rootCmd.AddCommand(NewEndpointCmd()) + rootCmd.AddCommand(addCommonCommand(NewEndpointCmd())) if err := cmd.Execute(); err != nil { log.Fatalf("failed to start begonia: %v", err) } diff --git a/config/settings.yml b/config/settings.yml index 0288fe7..0a5b32e 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -23,7 +23,7 @@ mysql: port: 3306 user: "test" password: "test" - database: "begonia-test" + database: "begonia-dev" table_prefix: "begonia_" admin: name: "admin" @@ -110,6 +110,13 @@ test: secretKey: "begoniabegonia" - name: "FILE_ENGINE_LOCAL" endpoint: "/tmp/upload" + mysql: + host: "127.0.0.1" + port: 3306 + user: "test" + password: "test" + database: "begonia-test" + table_prefix: "begonia_" etcd: endpoint: prefix: "/begonia/endpoints" diff --git a/gateway/endpoint_test.go b/gateway/endpoint_test.go index eba54f2..cd9522c 100644 --- a/gateway/endpoint_test.go +++ b/gateway/endpoint_test.go @@ -14,12 +14,7 @@ func TestRequest(t *testing.T) { go example.Run(":12148") time.Sleep(time.Second * 3) c.Convey("test request", t, func() { - // request := GrpcRequestImpl{ - // FullMethodName: "helloworld.Greeter/SayHello", - // In: &v1.HelloRequest{Msg: "begonia"}, - // Out: &v1.HelloReply{}, - // Ctx: context.Background(), - // } + request := NewGrpcRequest(context.Background(), nil, nil, "helloworld.Greeter/SayHello", WithIn(&v1.HelloRequest{Msg: "begonia"}), WithOut(&v1.HelloReply{})) pool := NewGrpcConnPool("127.0.0.1:12148") endpoint := NewEndpoint(pool) diff --git a/go.mod b/go.mod index a6c722b..7261fdd 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cockroachdb/errors v1.11.1 github.com/google/wire v0.6.0 github.com/smartystreets/goconvey v1.8.1 - github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4 + github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac github.com/spf13/cobra v1.8.0 google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 google.golang.org/grpc v1.64.0 diff --git a/go.sum b/go.sum index c344733..6660940 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoa github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -118,6 +119,7 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -161,6 +163,7 @@ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -202,6 +205,7 @@ github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -231,6 +235,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4 h1:HmuGbn+mhKpi5tM8iM5vpUUx4K+XZZdrPecNu8IeO8c= github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= +github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac h1:qGLujl2jYtkIeQYa2JEzQIQ5W3grnNcqVc2uuPV3uco= +github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= @@ -406,6 +412,7 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/internal/middleware/auth/aksk.go b/internal/middleware/auth/aksk.go index 1612a4a..006ea6b 100644 --- a/internal/middleware/auth/aksk.go +++ b/internal/middleware/auth/aksk.go @@ -63,8 +63,13 @@ func (a *AccessKeyAuthMiddleware) RequestBefore(ctx context.Context, info *grpc. if !ok { md = metadata.MD{} } - md.Set(gosdk.HeaderXIdentity, owner) + // md.Set(gosdk.HeaderXIdentity, owner) + md = metadata.Join(md, metadata.Pairs(gosdk.HeaderXIdentity, owner)) + ctx = metadata.NewIncomingContext(ctx, md) + // md2, _ := metadata.FromIncomingContext(ctx) + + return ctx, nil } diff --git a/internal/middleware/auth/apikey.go b/internal/middleware/auth/apikey.go index 0b9e9c9..6af3f66 100644 --- a/internal/middleware/auth/apikey.go +++ b/internal/middleware/auth/apikey.go @@ -48,7 +48,10 @@ func (a *ApiKeyAuthImpl) UnaryInterceptor(ctx context.Context, req any, info *gr if err != nil { return nil, gosdk.NewError(fmt.Errorf("query uid base on apikey get error:%w", err), int32(api.UserSvrCode_USER_APIKEY_NOT_MATCH_ERR), codes.Unauthenticated, "authorization_check") } - ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXIdentity, identity)) + md,_:=metadata.FromIncomingContext(ctx) + + md = metadata.Join(md, metadata.Pairs(gosdk.HeaderXIdentity, identity)) + ctx = metadata.NewIncomingContext(ctx, md) return handler(ctx, req) } return nil, err @@ -86,9 +89,11 @@ func (a *ApiKeyAuthImpl) ValidateStream(ctx context.Context, req interface{}, fu if err != nil { return ctx, gosdk.NewError(fmt.Errorf("query user id base on apikey err:%w", err), int32(api.UserSvrCode_USER_APIKEY_NOT_MATCH_ERR), codes.Unauthenticated, "authorization_check") } - ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXIdentity, identity)) + md,_:=metadata.FromIncomingContext(ctx) + + md = metadata.Join(md, metadata.Pairs(gosdk.HeaderXIdentity, identity)) headers.Set(strings.ToLower(gosdk.HeaderXIdentity), identity) - return ctx, err + return metadata.NewIncomingContext(ctx,md), err } return ctx, err } diff --git a/internal/middleware/auth/auth_test.go b/internal/middleware/auth/auth_test.go index 78727db..1abb2ae 100644 --- a/internal/middleware/auth/auth_test.go +++ b/internal/middleware/auth/auth_test.go @@ -90,8 +90,12 @@ func readInitAPP() (string, string, string) { log.Fatalf(err.Error()) return "", "", "" } + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } path := filepath.Join(homeDir, ".begonia") - path = filepath.Join(path, "admin-app.json") + path = filepath.Join(path, fmt.Sprintf("admin-app.%s.json", env)) file, err := os.Open(path) if err != nil { log.Fatalf(err.Error()) @@ -241,6 +245,12 @@ func TestUnaryInterceptor(t *testing.T) { if identify:=md.Get(gosdk.HeaderXIdentity);len(identify)==0||identify[0]==""{ return nil,fmt.Errorf("no app identity") } + XAccessKey:=md.Get(gosdk.HeaderXAccessKey) + XApiKey:=md.Get(gosdk.HeaderXApiKey) + XAuthz:=md.Get("authorization") + if len(XAccessKey)==0 && len(XApiKey)==0 && len(XAuthz)==0{ + return nil,fmt.Errorf("no app auth key") + } return nil, nil } R := routers.Get() @@ -342,7 +352,15 @@ func TestStreamInterceptor(t *testing.T) { ctx = metadata.NewIncomingContext(ctx, metadata.Pairs(gosdk.HeaderXApiKey, cnf.GetAdminAPIKey())) err = mid.StreamInterceptor(&v1.Users{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ctx: ctx}}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { - return nil + err:= ss.RecvMsg(srv) + md,_:=metadata.FromIncomingContext(ss.Context()) + if identify:=md.Get(gosdk.HeaderXIdentity);len(identify)==0||identify[0]==""{ + return fmt.Errorf("no app identity") + } + if xAppKey:=md.Get(gosdk.HeaderXApiKey);len(xAppKey)==0||xAppKey[0]==""{ + return fmt.Errorf("no app key") + } + return err }) c.So(err, c.ShouldBeNil) ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(gosdk.HeaderXApiKey, "cnf.GetAdminAPIKey()")) @@ -356,7 +374,15 @@ func TestStreamInterceptor(t *testing.T) { jwt := getJWT() ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "Bearer "+jwt)) err = mid.StreamInterceptor(&v1.Users{}, &greeterSayHelloWebsocketServer{ServerStream: &testStream{ctx: ctx}}, &grpc.StreamServerInfo{FullMethod: "/INTEGRATION.TESTSERVICE/GET"}, func(srv interface{}, ss grpc.ServerStream) error { - return ss.RecvMsg(srv) + err:= ss.RecvMsg(srv) + md,_:=metadata.FromIncomingContext(ss.Context()) + if identify:=md.Get(gosdk.HeaderXIdentity);len(identify)==0||identify[0]==""{ + return fmt.Errorf("no app identity") + } + if xAuthorization:=md.Get("authorization");len(xAuthorization)==0||xAuthorization[0]==""{ + return fmt.Errorf("no jwt key") + } + return err }) c.So(err, c.ShouldBeNil) ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("authorization", "Bearer 123")) @@ -399,6 +425,10 @@ func TestStreamInterceptor(t *testing.T) { if identify:=md.Get(gosdk.HeaderXIdentity);len(identify)==0||identify[0]==""{ return fmt.Errorf("no app identity") } + if xAccessKey:=md.Get(gosdk.HeaderXAccessKey);len(xAccessKey)==0||xAccessKey[0]==""{ + t.Logf("error metadata:%v",md) + return fmt.Errorf("no app access key") + } return err }) c.So(err, c.ShouldBeNil) diff --git a/internal/middleware/auth/headers.go b/internal/middleware/auth/headers.go index f03a1c1..b47c0f1 100644 --- a/internal/middleware/auth/headers.go +++ b/internal/middleware/auth/headers.go @@ -52,8 +52,9 @@ func (g *GrpcHeader) Release() { } func (g *GrpcHeader) Set(key, value string) { g.in.Set(key, value) - newCtx := metadata.NewIncomingContext(g.ctx, g.in) - g.ctx = newCtx + md, _ := metadata.FromIncomingContext(g.ctx) + newMd := metadata.Join(md, g.in) + g.ctx = metadata.NewIncomingContext(g.ctx, newMd) } func (g *GrpcHeader) SendHeader(key, value string) { diff --git a/internal/middleware/auth/jwt.go b/internal/middleware/auth/jwt.go index a90757c..00a6b54 100644 --- a/internal/middleware/auth/jwt.go +++ b/internal/middleware/auth/jwt.go @@ -168,8 +168,7 @@ func (a *JWTAuth) checkJWT(ctx context.Context, authorization string, rspHeader } func (a *JWTAuth) jwtValidator(ctx context.Context, headers Header) (context.Context, error) { - // 获取请求的方法名 - // 获取路由 + md, _ := metadata.FromIncomingContext(ctx) diff --git a/internal/middleware/http.go b/internal/middleware/http.go index c87c4d7..8bf0804 100644 --- a/internal/middleware/http.go +++ b/internal/middleware/http.go @@ -3,7 +3,6 @@ package middleware import ( "context" "fmt" - "log" "strings" "github.com/begonia-org/begonia/internal/pkg/routers" @@ -58,7 +57,6 @@ func (s *HttpStream) SendMsg(m interface{}) error { router := routersList.GetRouteByGrpcMethod(s.FullMethod) // 对内置服务的http响应进行格式化 if routersList.IsLocalSrv(s.FullMethod) || router.UseJsonResponse { - log.Printf("转换fullMethod:%v", s.FullMethod) rsp, _ := grpcToHttpResponse(m, nil) return s.ServerStream.SendMsg(rsp) } diff --git a/internal/migrate/app.go b/internal/migrate/app.go index f63a88f..48d78d2 100644 --- a/internal/migrate/app.go +++ b/internal/migrate/app.go @@ -23,7 +23,7 @@ type APPOperator struct { func NewAPPOperator(mysql *tiga.MySQLDao) *APPOperator { return &APPOperator{mysql: mysql} } -func dumpInitApp(app *api.Apps) error { +func dumpInitApp(app *api.Apps,env string) error { log.Print("########################################admin-app###############################") log.Printf("Init appid:%s", app.Appid) log.Printf("Init accessKey:%s", app.AccessKey) @@ -36,7 +36,7 @@ func dumpInitApp(app *api.Apps) error { if err := os.MkdirAll(path, os.ModePerm); err != nil { return err } - file, err := os.Create(filepath.Join(path, "admin-app.json")) + file, err := os.Create(filepath.Join(path, fmt.Sprintf("admin-app.%s.json",env))) if err != nil { return err } @@ -49,11 +49,11 @@ func dumpInitApp(app *api.Apps) error { log.Print("#################################################################################") return nil } -func (m *APPOperator) InitAdminAPP(owner string) (err error) { +func (m *APPOperator) InitAdminAPP(owner,env string) (err error) { app := &api.Apps{} defer func() { if app.Appid != "" { - if errInit := dumpInitApp(app); errInit != nil { + if errInit := dumpInitApp(app,env); errInit != nil { err = errInit } } diff --git a/internal/migrate/operator.go b/internal/migrate/operator.go index f1722e0..b453808 100644 --- a/internal/migrate/operator.go +++ b/internal/migrate/operator.go @@ -29,11 +29,12 @@ func (m *InitOperator) Init() error { phone := m.config.GetDefaultAdminPhone() aseKey := m.config.GetAesKey() ivKey := m.config.GetAesIv() + env:=m.config.GetEnv() uid, err := m.user.InitAdminUser(adminPasswd, aseKey, ivKey, name, email, phone) if err != nil { log.Printf("failed to init admin user: %v", err) return err } - return m.app.InitAdminAPP(uid) + return m.app.InitAdminAPP(uid,env) } diff --git a/internal/migrate/operator_test.go b/internal/migrate/operator_test.go index 7fa53b6..b41cbc2 100644 --- a/internal/migrate/operator_test.go +++ b/internal/migrate/operator_test.go @@ -126,7 +126,7 @@ func TestAppOperatorFail(t *testing.T) { } patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("first failed")) defer patch.Reset() - err := operator.InitAdminAPP("test") + err := operator.InitAdminAPP("test",env) patch.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "first failed") @@ -137,7 +137,7 @@ func TestAppOperatorFail(t *testing.T) { for _, caseV := range cases { patch2 := gomonkey.ApplyFuncReturn(caseV.patch, caseV.output...) defer patch2.Reset() - err := operator.InitAdminAPP("test") + err := operator.InitAdminAPP("test",env) patch2.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, caseV.err.Error()) diff --git a/internal/service/base_test.go b/internal/service/base_test.go index 527bf41..f1c0933 100644 --- a/internal/service/base_test.go +++ b/internal/service/base_test.go @@ -3,6 +3,7 @@ package service_test import ( "context" "encoding/json" + "fmt" "log" "os" "path/filepath" @@ -57,7 +58,7 @@ func readInitAPP() { op := internal.InitOperatorApp(config.ReadConfig(env)) _ = op.Init() path := filepath.Join(homeDir, ".begonia") - path = filepath.Join(path, "admin-app.json") + path = filepath.Join(path, fmt.Sprintf("admin-app.%s.json",env)) file, err := os.Open(path) if err != nil { From 95280405d8775190bd7cf2f417389c810ba3b2b9 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Tue, 18 Jun 2024 00:26:59 +0800 Subject: [PATCH 14/16] chore:use test env in uint test --- .github/workflows/go.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d49a6ba..89381f3 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -29,8 +29,8 @@ jobs: - name: Init MySQL run: | go mod tidy - go run ./cmd/begonia init -c ./config/settings.yml -e dev - cat ~/.begonia/admin-app.json + go run ./cmd/begonia init -c ./config/settings.yml -e test + cat ~/.begonia/admin-app.test.json - name: Init Minio run: | go install github.com/minio/mc@latest From 405cc74d41f9c3cd8a5aa527da5c909bb46006bf Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Fri, 28 Jun 2024 15:33:09 +0800 Subject: [PATCH 15/16] feat:add minio fs --- gateway/formdata.go | 6 +- gateway/http.go | 2 + gateway/middlewares.go | 12 +-- go.mod | 6 +- go.sum | 6 ++ internal/biz/file/file.go | 77 ++++++++++------- internal/biz/file/file_test.go | 143 +++++++++++++++++++++++++------- internal/biz/file/minio.go | 45 ++++++---- internal/biz/file/minio_test.go | 75 +++++++++++++---- internal/data/file.go | 17 ++-- internal/data/file_test.go | 65 +++++++++------ internal/service/file.go | 2 +- 12 files changed, 324 insertions(+), 132 deletions(-) diff --git a/gateway/formdata.go b/gateway/formdata.go index dc3ff77..ac5c966 100644 --- a/gateway/formdata.go +++ b/gateway/formdata.go @@ -35,6 +35,7 @@ type FormDataDecoder struct { } func (f *FormDataMarshaler) NewDecoder(r io.Reader) runtime.Decoder { + // log.Printf("new form data decoder") return &FormDataDecoder{r: r} } @@ -45,14 +46,14 @@ func (f *FormDataDecoder) Decode(v interface{}) error { } } - + // log.Printf("decode form-data") // 使用multipart.Reader来解析formData reader := multipart.NewReader(f.r, f.boundary) formData, err := reader.ReadForm(32 << 20) // 32MB是formData的最大内存使用 if err != nil && err != io.EOF { return err } - + // log.Printf("form data file:%v", len(formData.File)) for key, files := range formData.File { file := files[0] fd, err := file.Open() @@ -68,6 +69,7 @@ func (f *FormDataDecoder) Decode(v interface{}) error { if _, ok := formData.Value[key]; !ok { formData.Value[key] = make([]string, 0) } + // log.Printf("recv file %s, size %d", file.Filename, len(fileBytes)) formData.Value[key] = append(formData.Value[key], string(fileBytes)) } diff --git a/gateway/http.go b/gateway/http.go index 02ed5e1..fc01fc3 100644 --- a/gateway/http.go +++ b/gateway/http.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "log" "net/http" "os" "strings" @@ -385,6 +386,7 @@ func (h *HttpEndpointImpl) RegisterHandlerClient(ctx context.Context, pd Protobu if req.Header.Get("accept") == "" || req.Header.Get("accept") == "*/*" { req.Header.Set("accept", "application/json") } + log.Printf("request content-type:%s", req.Header.Get("content-type")) ctx, cancel := context.WithCancel(req.Context()) defer cancel() inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) diff --git a/gateway/middlewares.go b/gateway/middlewares.go index 46e8e89..0556228 100644 --- a/gateway/middlewares.go +++ b/gateway/middlewares.go @@ -2,6 +2,7 @@ package gateway import ( "context" + "fmt" "net/http" "strconv" "strings" @@ -118,23 +119,23 @@ func IncomingHeadersToMetadata(ctx context.Context, req *http.Request) metadata. accessKey := md.Get(XAccessKey) apikey:=md.Get(XApiKey) author := "" - idType := gosdk.UidType + // idType := gosdk.UidType if len(xuid) > 0 { author = xuid[0] } if author == "" && len(accessKey) > 0 { author = accessKey[0] - idType = gosdk.AccessKeyType + // idType = gosdk.AccessKeyType } if author == ""&& len(apikey)>0{ author = apikey[0] - idType = gosdk.ApiKeyType + // idType = gosdk.ApiKeyType } if author == "" { return md } - md.Set(XIdentity, author) - md.Set(XIdentityType, idType) + // md.Set(XIdentity, author) + // md.Set(XIdentityType, idType) return md } @@ -247,6 +248,7 @@ func HandleErrorWithLogger(logger logger.Logger) runtime.ErrorHandlerFunc { "status": statusCode, }, ) + fmt.Printf("error type:%T, error:%v", err, err) if _, ok := metadata.FromIncomingContext(ctx); !ok { md := IncomingHeadersToMetadata(ctx, req) ctx = metadata.NewIncomingContext(ctx, md) diff --git a/go.mod b/go.mod index 7261fdd..9f640da 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/smartystreets/goconvey v1.8.1 github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac github.com/spf13/cobra v1.8.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 + google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.2 ) @@ -80,7 +80,7 @@ require ( require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f + github.com/begonia-org/go-sdk v0.0.0-20240628071225-2864d45934ea github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 @@ -139,7 +139,7 @@ require ( go.uber.org/zap v1.27.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 6660940..3204baa 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,8 @@ github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f h1:1roWosaPjA7vE5CnnF6irDWt05SLD57bsHLun4HYeLU= github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240628071225-2864d45934ea h1:jdDBLZVsGKfmF/V+U4WUrz4Hzd3/62GqMIly9gnSppw= +github.com/begonia-org/go-sdk v0.0.0-20240628071225-2864d45934ea/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -401,8 +403,12 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= +google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= +google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/internal/biz/file/file.go b/internal/biz/file/file.go index 8ef18d9..632dc9f 100644 --- a/internal/biz/file/file.go +++ b/internal/biz/file/file.go @@ -6,7 +6,6 @@ import ( goErr "errors" "fmt" "io" - "log" "net/http" "os" "path/filepath" @@ -31,9 +30,9 @@ import ( type FileRepo interface { // mysql - UpsertFile(ctx context.Context, file *api.Files) error + UpsertFile(ctx context.Context, file *api.Files) (bool, error) DelFile(ctx context.Context, engine, bucket, key string) error - UpsertBucket(ctx context.Context, bucket *api.Buckets) error + UpsertBucket(ctx context.Context, bucket *api.Buckets) (bool, error) DelBucket(ctx context.Context, bucketId string) error GetFileById(ctx context.Context, fid string) (*api.Files, error) List(ctx context.Context, page, pageSize int32, bucket, engine, owner string) ([]*api.Files, error) @@ -50,7 +49,7 @@ type FileUsecase interface { Version(ctx context.Context, bucket, key, authorId string) (string, error) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) - MakeBucket(ctx context.Context, in *api.MakeBucketRequest,authorId string) (*api.MakeBucketResponse, error) + MakeBucket(ctx context.Context, in *api.MakeBucketRequest, authorId string) (*api.MakeBucketResponse, error) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) GetFileByID(ctx context.Context, fileId string) (*api.Files, error) } @@ -131,14 +130,14 @@ func getSHA256(data []byte) string { func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId string, authorEmail string) (commitId string, err error) { repo, err := git.PlainInit(dir, false) if err != nil && err != git.ErrRepositoryAlreadyExists { - return "", fmt.Errorf("version plain init err:%w",err) + return "", fmt.Errorf("version plain init err:%w", err) } // 如果仓库已存在,则打开它 if err == git.ErrRepositoryAlreadyExists { repo, err = git.PlainOpen(dir) if err != nil { - return "", fmt.Errorf("version plain open err:%w",err) + return "", fmt.Errorf("version plain open err:%w", err) } } @@ -152,7 +151,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin // 工作树 w, err := repo.Worktree() if err != nil { - return "", fmt.Errorf("version work tree err:%w",err) + return "", fmt.Errorf("version work tree err:%w", err) } defer func() { if p := recover(); p != nil { @@ -166,7 +165,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin // 添加文件到暂存区 _, err = w.Add(filename) if err != nil { - return "", fmt.Errorf("add file err:%w",err) + return "", fmt.Errorf("add file err:%w", err) } // 创建提交 @@ -174,7 +173,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin Author: author, }) if err != nil && !goErr.Is(err, git.ErrEmptyCommit) { - return "", fmt.Errorf("commit file err:%w",err) + return "", fmt.Errorf("commit file err:%w", err) } // 空提交处理 if goErr.Is(err, git.ErrEmptyCommit) { @@ -189,7 +188,7 @@ func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId strin obj, err := repo.CommitObject(commit) if err != nil { - return "", fmt.Errorf("get commit object err:%w",err) + return "", fmt.Errorf("get commit object err:%w", err) } return obj.ID().String(), nil @@ -216,33 +215,36 @@ func (f *FileUsecaseImpl) checkIn(key string) (string, error) { } return key, nil } -func (f *FileUsecaseImpl) MakeBucket(ctx context.Context, in *api.MakeBucketRequest,authorId string) (*api.MakeBucketResponse, error) { +func (f *FileUsecaseImpl) MakeBucket(ctx context.Context, in *api.MakeBucketRequest, authorId string) (*api.MakeBucketResponse, error) { + saveDir := filepath.Join(f.config.GetUploadDir(), in.Bucket) if err := os.MkdirAll(saveDir, 0755); err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket") } - if in.EnableVersion{ - _,_=os.Create(filepath.Join(saveDir, ".gitkeep")) - _,err:=f.commitFile(saveDir, ".gitkeep", authorId, "fs@begonia.com") + if in.EnableVersion { + _, _ = os.Create(filepath.Join(saveDir, ".gitkeep")) + _, err := f.commitFile(saveDir, ".gitkeep", authorId, "fs@begonia.com") if err != nil { - return nil, gosdk.NewError(fmt.Errorf("init bucket version err:%w",err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") - + return nil, gosdk.NewError(fmt.Errorf("init bucket version err:%w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") + } } + return &api.MakeBucketResponse{}, nil } func (f *FileUsecaseImpl) checkBucket(bucket string) bool { saveDir := filepath.Join(f.config.GetUploadDir(), bucket) return pathExists(saveDir) } -func (f *FileUsecaseImpl)isEnableVersion(bucket string)bool{ - path:=filepath.Join(f.config.GetUploadDir(), bucket) +func (f *FileUsecaseImpl) isEnableVersion(bucket string) bool { + path := filepath.Join(f.config.GetUploadDir(), bucket) repo, err := git.PlainOpen(path) - if err!=nil||repo==nil{ + if err != nil || repo == nil { return false } return true } + // Upload uploads a file. // // The file is saved in the directory specified by the key. @@ -315,15 +317,24 @@ func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), } - err = f.repo.UpsertFile(ctx, fileObj) + updated, err := f.repo.UpsertFile(ctx, fileObj) if err != nil { return nil, err } - log.Printf("上传文件:%s成功", filePath) + uid := fileObj.Uid + if updated { + existsObj, err := f.repo.GetFile(ctx, fileObj.Engine, fileObj.Bucket, fileObj.Key) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("get updated file error:%w",err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") + } + if existsObj != nil { + uid = existsObj.Uid + } + } return &api.UploadFileResponse{ Uri: uri, Version: commitId, - Uid: fileObj.Uid, + Uid: uid, }, err } @@ -465,7 +476,7 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a if err != nil { return nil, err } - if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok && in.Engine==api.FileEngine_FILE_ENGINE_LOCAL.String() { + if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok && in.Engine == api.FileEngine_FILE_ENGINE_LOCAL.String() { return nil, gosdk.NewError(pkg.ErrBucketNotFound, int32(api.FileSvrStatus_FILE_INVALIDATE_BUCKET_ERR), codes.NotFound, "bucket_not_found") } @@ -507,7 +518,7 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a } commit := "" - if f.isEnableVersion(in.Bucket)&& in.Engine==api.FileEngine_FILE_ENGINE_LOCAL.String() { + if f.isEnableVersion(in.Bucket) && in.Engine == api.FileEngine_FILE_ENGINE_LOCAL.String() { commit, err = f.commitFile(saveDir, filename, authorId, "fs@begonia.com") if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") @@ -527,17 +538,27 @@ func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *a CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), } - log.Printf("insert %s,%s,%s,%s", fileObj.Uid, fileObj.Bucket, fileObj.Key, fileObj.Engine) - err = f.repo.UpsertFile(ctx, fileObj) + // log.Printf("insert %s,%s,%s,%s", fileObj.Uid, fileObj.Bucket, fileObj.Key, fileObj.Engine) + updated, err := f.repo.UpsertFile(ctx, fileObj) if err != nil { - return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") + return nil, gosdk.NewError(fmt.Errorf("insert or update file err:%w",err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") + } + uid := fileObj.Uid + if updated { + existsObj, err := f.repo.GetFile(ctx, fileObj.Engine, fileObj.Bucket, fileObj.Key) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("get updated file error:%w",err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") + } + if existsObj != nil { + uid = existsObj.Uid + } } os.RemoveAll(filepath.Join(f.config.GetUploadDir(), in.UploadId)) return &api.CompleteMultipartUploadResponse{ Uri: uri, Version: commit, - Uid: fileObj.Uid, + Uid: uid, }, err } diff --git a/internal/biz/file/file_test.go b/internal/biz/file/file_test.go index a3d4cb3..032bc6e 100644 --- a/internal/biz/file/file_test.go +++ b/internal/biz/file/file_test.go @@ -143,7 +143,7 @@ func testMkBucket(t *testing.T) { bucket = fmt.Sprintf("bucket-biz-%s", time.Now().Format("20060102150405")) c.Convey("test make bucket success", t, func() { rsp, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ - Bucket: bucket, + Bucket: bucket, EnableVersion: true, }, fileAuthor) c.So(err, c.ShouldBeNil) @@ -154,17 +154,17 @@ func testMkBucket(t *testing.T) { defer patch.Reset() _, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ Bucket: "", - },fileAuthor) + }, fileAuthor) patch.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "mkdir error") - patch1:=gomonkey.ApplyFuncReturn(git.PlainInit, nil, fmt.Errorf("init error")) + patch1 := gomonkey.ApplyFuncReturn(git.PlainInit, nil, fmt.Errorf("init error")) defer patch1.Reset() _, err = fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ - Bucket: "test-2", + Bucket: "test-2", EnableVersion: true, - },fileAuthor) + }, fileAuthor) patch1.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "init error") @@ -200,7 +200,6 @@ func testPutFile(t *testing.T) { cnf := cfg.NewConfig(config) fileSha256 = tmp.sha256 // bucket = fmt.Sprintf("bucket-biz-%s", time.Now().Format("20060102150405")) - cases := []fileUploadTestCase{ { title: "test upload without version", @@ -478,7 +477,6 @@ func testDownload(t *testing.T) { Key: _case.key, Bucket: bucket, Version: _case.version, - }, _case.author) if _case.exceptErr != nil { c.So(err, c.ShouldNotBeNil) @@ -775,13 +773,14 @@ func testCompleteMultipartUploadFile(t *testing.T) { } config := config.ReadConfig(env) cnf := cfg.NewConfig(config) + fid := "" c.Convey("test complete parts file success", t, func() { rsp, err := fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ - Key: "test/upload.parts.test1", - UploadId: uploadId, - Sha256: bigFileSha256, - Bucket: bucket, - Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + Key: "test/upload.parts.test1", + UploadId: uploadId, + Sha256: bigFileSha256, + Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) @@ -795,7 +794,53 @@ func testCompleteMultipartUploadFile(t *testing.T) { t.Error(err) } c.So(sha, c.ShouldEqual, bigFileSha256) + fid = rsp.Uid + + }) + c.Convey("test complete parts file update", t, func() { + bigTmpFile2, _ := generateRandomFile(1024 * 1024 * 12) + defer os.Remove(bigTmpFile2.path) + rsp, err := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ + Key: "test/upload.parts.test1", + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + }) + c.So(err, c.ShouldBeNil) + _=uploadParts(bigTmpFile2.path, rsp.UploadId, "test/upload.parts.test1", t) + rsp2, err := fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ + Key: "test/upload.parts.test1", + UploadId: rsp.UploadId, + Sha256: bigTmpFile2.sha256, + Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + }, fileAuthor) + + c.So(err, c.ShouldBeNil) + c.So(rsp2.Uid, c.ShouldEqual,fid) + }) + c.Convey("test complete parts file update fail", t, func() { + bigTmpFile2, _ := generateRandomFile(1024 * 1024 * 12) + defer os.Remove(bigTmpFile2.path) + rsp, err := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ + Key: "test/upload.parts.test1", + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + }) + c.So(err, c.ShouldBeNil) + + _=uploadParts(bigTmpFile2.path, rsp.UploadId, "test/upload.parts.test1", t) + patch:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("remove error")) + defer patch.Reset() + _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ + Key: "test/upload.parts.test1", + UploadId: rsp.UploadId, + Sha256: bigTmpFile2.sha256, + Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + }, fileAuthor) + patch.Reset() + bigFileSha256 = bigTmpFile2.sha256 + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get updated file error") }) c.Convey("test complete parts file fail", t, func() { @@ -829,9 +874,9 @@ func testCompleteMultipartUploadFile(t *testing.T) { }) uploadId2 := rsp.UploadId _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ - Key: "test/upload.parts.test2", - UploadId: uploadId2, - Bucket: bucket, + Key: "test/upload.parts.test2", + UploadId: uploadId2, + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "lstat error") @@ -887,10 +932,10 @@ func testCompleteMultipartUploadFile(t *testing.T) { patch2 := gomonkey.ApplyFuncReturn(cases.patch, cases.output...) defer patch2.Reset() _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ - Key: "test/upload.parts.test2", - UploadId: uploadId3, - Bucket: bucket, - Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + Key: "test/upload.parts.test2", + UploadId: uploadId3, + Bucket: bucket, + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) patch2.Reset() t.Logf("error:%v", cases.err) @@ -908,7 +953,7 @@ func testFileMeta(t *testing.T) { Key: "test/upload.parts.test1", Version: "", Bucket: bucket, - Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(meta, c.ShouldNotBeNil) @@ -919,7 +964,7 @@ func testFileMeta(t *testing.T) { Key: "test/upload.parts.test1", Version: "latest", Bucket: bucket, - Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), + Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(meta, c.ShouldNotBeNil) @@ -975,7 +1020,7 @@ func testFileMeta(t *testing.T) { c.So(err.Error(), c.ShouldContainSubstring, "io.copy error") patch3.Reset() - patch4:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) + patch4 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("get error")) defer patch4.Reset() _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ Key: "test/upload.parts.test1", @@ -1241,7 +1286,7 @@ func testDelete(t *testing.T) { c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "not found") - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key:"/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) @@ -1351,21 +1396,57 @@ func testUploadVersionCommitErr(t *testing.T) { }) } -func testGetFileByID(t *testing.T){ - fileBiz:=newFileBiz() - c.Convey("test get file by id",t,func(){ - rsp,err:=fileBiz.GetFileByID(context.TODO(),fileId) - c.So(err,c.ShouldBeNil) - c.So(rsp,c.ShouldNotBeNil) +func testGetFileByID(t *testing.T) { + fileBiz := newFileBiz() + rsp, err := fileBiz.GetFileByID(context.TODO(), fileId) + + c.Convey("test get file by id", t, func() { + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + }) + c.Convey("test file update", t, func() { + tmp5, _ := generateRandomFile(1024 * 1024 * 1) + defer os.Remove(tmp5.path) + rsp2, err := fileBiz.Upload(context.Background(), &api.UploadFileRequest{ + Key: rsp.Key, + Engine: rsp.Engine, + Bucket: rsp.Bucket, + Content: tmp5.content, + ContentType: tmp5.contentType, + Sha256: tmp5.sha256, + }, fileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp2, c.ShouldNotBeNil) + c.So(rsp2.Uid, c.ShouldEqual, rsp.Uid) + + }) + + c.Convey("test file update fail", t, func() { + tmp5, _ := generateRandomFile(1024 * 1024 * 1) + defer os.Remove(tmp5.path) + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("get error")) + defer patch.Reset() + _, err := fileBiz.Upload(context.Background(), &api.UploadFileRequest{ + Key: rsp.Key, + Engine: rsp.Engine, + Bucket: rsp.Bucket, + Content: tmp5.content, + ContentType: tmp5.contentType, + Sha256: tmp5.sha256, + }, fileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get updated file error") + }) } func TestFile(t *testing.T) { t.Run("test make bucket", testMkBucket) t.Run("test upload", testPutFile) - t.Run("test get file by id",testGetFileByID) - t.Run("test upload version commit error", testUploadVersionCommitErr) t.Run("test download", testDownload) + t.Run("test get file by id", testGetFileByID) + t.Run("test upload version commit error", testUploadVersionCommitErr) t.Run("test initiate upload file", testInitiateUploadFile) t.Run("test upload parts file", testUploadMultipartFileFile) t.Run("test complete parts file", testCompleteMultipartUploadFile) diff --git a/internal/biz/file/minio.go b/internal/biz/file/minio.go index 19e59e6..e6b4fc7 100644 --- a/internal/biz/file/minio.go +++ b/internal/biz/file/minio.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "fmt" "io" + "log" "os" "path/filepath" @@ -28,15 +29,15 @@ type MinioUseCase struct { func NewMinioUseCase(minioClient *minio.Client, localFile *FileUsecaseImpl) FileUsecase { return &MinioUseCase{minioClient: minioClient, localFile: localFile} } -func (m *MinioUseCase) MakeBucket(ctx context.Context, in *api.MakeBucketRequest,_ string) (*api.MakeBucketResponse, error) { +func (m *MinioUseCase) MakeBucket(ctx context.Context, in *api.MakeBucketRequest, _ string) (*api.MakeBucketResponse, error) { err := m.minioClient.MakeBucket(ctx, in.Bucket, minio.MakeBucketOptions{Region: in.Region, ObjectLocking: in.ObjectLocking}) if err != nil { - return nil, gosdk.NewError(fmt.Errorf("failed to make bucket: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket",gosdk.WithClientMessage(err.Error())) + return nil, gosdk.NewError(fmt.Errorf("failed to make bucket: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket", gosdk.WithClientMessage(err.Error())) } err = m.minioClient.EnableVersioning(ctx, in.Bucket) - if err!=nil{ - return nil, gosdk.NewError(fmt.Errorf("failed to enable versioning: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "enable_versioning",gosdk.WithClientMessage(err.Error())) - + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to enable versioning: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "enable_versioning", gosdk.WithClientMessage(err.Error())) + } return &api.MakeBucketResponse{}, nil @@ -47,19 +48,18 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au return nil, gosdk.NewError(fmt.Errorf("failed to check bucket: %w or bucket %s not exist", err, in.Bucket), int32(common.Code_INTERNAL_ERROR), codes.Internal, "check_bucket") } contentLength := len(in.Content) - // if in.UseVersion { - // _ = m.minioClient.EnableVersioning(ctx, in.Bucket) - // } + + in.Engine = api.FileEngine_FILE_ENGINE_MINIO.String() hash := sha256.New() hash.Write([]byte(in.Content)) checksum := hash.Sum(nil) - // checksumHex := hex.EncodeToString(checksum) // 设置对象元数据,包括 SHA-256 校验和 userMetadata := map[string]string{ "x-amz-checksum-algorithm": "SHA256", "x-amz-checksum-sha256": base64.StdEncoding.EncodeToString(checksum), } + log.Printf("Put object %s to %s,and its size is %d", in.Key, in.Bucket, contentLength) info, err := m.minioClient.PutObject(ctx, in.Bucket, in.Key, bytes.NewReader(in.Content), int64(contentLength), minio.PutObjectOptions{ContentType: in.ContentType, DisableContentSha256: false, UserMetadata: userMetadata}) if err != nil { return nil, gosdk.NewError(fmt.Errorf("failed to upload object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upload_object") @@ -67,7 +67,7 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au } f := &api.Files{ Uid: m.localFile.snowflake.GenerateIDString(), - Engine: api.FileEngine_name[int32(*api.FileEngine_FILE_ENGINE_MINIO.Enum())], + Engine: in.Engine, Bucket: in.Bucket, Key: in.Key, IsDeleted: false, @@ -75,27 +75,41 @@ func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, au CreatedAt: timestamppb.Now(), UpdatedAt: timestamppb.Now(), } - err = m.localFile.repo.UpsertFile(ctx, f) + updated, err := m.localFile.repo.UpsertFile(ctx, f) if err != nil { return nil, gosdk.NewError(fmt.Errorf("failed to upsert file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") } + + uid := f.Uid + if updated { + existsObj, err := m.localFile.repo.GetFile(ctx, f.Engine, f.Bucket, f.Key) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to get updated file info:%w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") + } + if existsObj != nil { + uid = existsObj.Uid + } + } // log.Printf("upload object:%s,%s", info.Key, info.ChecksumSHA256) - return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID, Uid: f.Uid}, err + return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID, Uid: uid}, err } func (m *MinioUseCase) InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { + in.Engine = api.FileEngine_FILE_ENGINE_MINIO.String() return m.localFile.InitiateUploadFile(ctx, in) } func (m *MinioUseCase) UploadMultipartFileFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) { + in.Engine = api.FileEngine_FILE_ENGINE_MINIO.String() return m.localFile.UploadMultipartFileFile(ctx, in) } func (m *MinioUseCase) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { + in.Engine = api.FileEngine_FILE_ENGINE_MINIO.String() return m.localFile.AbortMultipartUpload(ctx, in) } func (m *MinioUseCase) CompleteMultipartUploadFile(ctx context.Context, in *api.CompleteMultipartUploadRequest, authorId string) (*api.CompleteMultipartUploadResponse, error) { // _, _ = m.localFile.MakeBucket(ctx, &api.MakeBucketRequest{Bucket: in.Bucket},authorId) originKey := in.Key - + in.Engine = api.FileEngine_FILE_ENGINE_MINIO.String() rsp, err := m.localFile.CompleteMultipartUploadFile(ctx, in, authorId) if err != nil { return nil, err @@ -173,6 +187,7 @@ func (m *MinioUseCase) Metadata(ctx context.Context, in *api.FileMetadataRequest bucket := in.Bucket if fileId == "" { + // log.Printf("fileId is empty,try to get file info from local file,%s,%s,%s", in.Engine, in.Bucket, in.Key) file, err := m.localFile.repo.GetFile(ctx, in.Engine, in.Bucket, in.Key) if err != nil { return nil, gosdk.NewError(fmt.Errorf("failed to get file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file") @@ -200,7 +215,7 @@ func (m *MinioUseCase) Version(ctx context.Context, bucket, key, authorId string return info.VersionID, nil } func (m *MinioUseCase) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) { - object, err := m.minioClient.GetObject(ctx, in.Bucket, in.Key, minio.GetObjectOptions{VersionID: in.Version}) + object, err := m.minioClient.GetObject(ctx, in.Bucket, in.Key, minio.GetObjectOptions{}) if err != nil { return nil, gosdk.NewError(fmt.Errorf("failed to get object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object") } @@ -215,6 +230,7 @@ func (m *MinioUseCase) Download(ctx context.Context, in *api.DownloadRequest, au return nil, gosdk.NewError(fmt.Errorf("failed to read object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "read_object") } + // log.Printf("download object:%s,size:%d,%v", in.Key,len(buf),stat.Size) return buf, nil } func (m *MinioUseCase) Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) { @@ -225,6 +241,7 @@ func (m *MinioUseCase) Delete(ctx context.Context, in *api.DeleteRequest, author return &api.DeleteResponse{}, nil } func (m *MinioUseCase) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) { + in.Engine = api.FileEngine_FILE_ENGINE_MINIO.String() return m.localFile.List(ctx, in, authorId) } func (m *MinioUseCase) GetFileByID(ctx context.Context, fileId string) (*api.Files, error) { diff --git a/internal/biz/file/minio_test.go b/internal/biz/file/minio_test.go index 0e4b69d..590b5f7 100644 --- a/internal/biz/file/minio_test.go +++ b/internal/biz/file/minio_test.go @@ -29,6 +29,7 @@ var minioFileAuthor = "" var sha256Str = "" var minioUploadId = "" var minioBigFileSha256 = "" +var minioFileId = "" func newFileMinioBiz() file.FileUsecase { env := "dev" @@ -50,9 +51,9 @@ func testMinioMkBucket(t *testing.T) { minioBucket = fmt.Sprintf("bucket-minio-biz-%s", time.Now().Format("20060102150405")) c.Convey("test make bucket success", t, func() { rsp, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ - Bucket: minioBucket, + Bucket: minioBucket, EnableVersion: true, - },minioFileAuthor) + }, minioFileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) }) @@ -61,7 +62,7 @@ func testMinioMkBucket(t *testing.T) { defer patch.Reset() _, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ Bucket: "", - },minioFileAuthor) + }, minioFileAuthor) patch.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "mkdir error") @@ -69,9 +70,9 @@ func testMinioMkBucket(t *testing.T) { patch2 := gomonkey.ApplyFuncReturn((*minio.Client).EnableVersioning, fmt.Errorf("enable error")) defer patch2.Reset() _, err = fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ - Bucket: fmt.Sprintf("bucket-minio-biz-4-%s", time.Now().Format("20060102150405")), + Bucket: fmt.Sprintf("bucket-minio-biz-4-%s", time.Now().Format("20060102150405")), EnableVersion: true, - },minioFileAuthor) + }, minioFileAuthor) patch2.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "enable error") @@ -83,16 +84,20 @@ func testMinioUpload(t *testing.T) { fileBiz := newFileMinioBiz() c.Convey("test upload file success", t, func() { + t.Logf("minio author:%s", minioFileAuthor) rsp, err := fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ - Bucket: minioBucket, - Key: "test.txt", - Content: []byte("hello"), + Bucket: minioBucket, + Key: "test.txt", + Content: []byte("hello"), + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioFileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) shaer := sha256.New() shaer.Write([]byte("hello")) sha256Str = hex.EncodeToString(shaer.Sum(nil)) + + minioFileId = rsp.Uid }) c.Convey("test upload file fail", t, func() { patch := gomonkey.ApplyFuncReturn((*minio.Client).PutObject, nil, fmt.Errorf("upload error")) @@ -101,6 +106,7 @@ func testMinioUpload(t *testing.T) { Bucket: minioBucket, Key: "test.txt", Content: []byte("hello"), + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioFileAuthor) patch.Reset() c.So(err, c.ShouldNotBeNil) @@ -113,7 +119,7 @@ func testMinioUpload(t *testing.T) { c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "not exist") - patch2 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.Upsert, fmt.Errorf("upsert error")) + patch2 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.Upsert, false, fmt.Errorf("upsert error")) defer patch2.Reset() _, err = fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ Bucket: minioBucket, @@ -169,9 +175,39 @@ func testMinioDownload(t *testing.T) { c.So(err.Error(), c.ShouldContainSubstring, "stat error") }) } +func testFileUpdated(t *testing.T) { + fileBiz := newFileMinioBiz() + + c.Convey("test file updated", t, func() { + rsp, err := fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ + Bucket: minioBucket, + Key: "test.txt", + Content: []byte("hello world"), + }, minioFileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + c.So(rsp.Uid, c.ShouldEqual, minioFileId) + shaer := sha256.New() + shaer.Write([]byte("hello world")) + sha256Str = hex.EncodeToString(shaer.Sum(nil)) + + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("get error")) + defer patch.Reset() + _, err = fileBiz.Upload(context.Background(), &api.UploadFileRequest{ + Key: "test.txt", + Bucket: minioBucket, + Content: []byte("hello world"), + }, minioFileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "failed to get updated file info") + + }) +} func testMinioMeta(t *testing.T) { fileBiz := newFileMinioBiz() c.Convey("test get meta success", t, func() { + t.Logf("minio bucket:%s", minioBucket) rsp, err := fileBiz.Metadata(context.TODO(), &api.FileMetadataRequest{ Bucket: minioBucket, Key: "test.txt", @@ -193,11 +229,11 @@ func testMinioMeta(t *testing.T) { c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "get meta error") - patch4:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) + patch4 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("get error")) defer patch4.Reset() _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ - Key: "test.txt", - Bucket: minioBucket, + Key: "test.txt", + Bucket: minioBucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "get error") @@ -225,6 +261,7 @@ func testMinioInitPartsUpload(t *testing.T) { c.Convey("test init parts upload success", t, func() { rsp, err := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ Key: "test-minio.txt", + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) @@ -287,7 +324,7 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { Bucket: minioBucket, ContentType: "text/plain", Sha256: minioBigFileSha256, - Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) patchx.Reset() c.So(err, c.ShouldNotBeNil) @@ -300,7 +337,7 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { Bucket: "", ContentType: "text/plain", Sha256: minioBigFileSha256, - Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrBucketNotFound.Error()) @@ -313,7 +350,7 @@ func testMinioCompleteMultipartUploadFile(t *testing.T) { Bucket: minioBucket, ContentType: "text/plain", Sha256: minioBigFileSha256, - Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), }, minioUploadId) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) @@ -440,13 +477,14 @@ func testMinioDelete(t *testing.T) { func testMinioList(t *testing.T) { fileBiz := newFileMinioBiz() c.Convey("test list success", t, func() { - rsp, err := fileBiz.List(context.Background(), &api.ListFilesRequest{Bucket: minioBucket, Page: 1, PageSize: 20}, minioFileAuthor) + t.Logf("minio bucket:%s,engine:%s,author:%s", minioBucket,api.FileEngine_FILE_ENGINE_MINIO.String(),minioFileAuthor) + rsp, err := fileBiz.List(context.Background(), &api.ListFilesRequest{Bucket: minioBucket, Page: 1, PageSize: 20, Engine: api.FileEngine_FILE_ENGINE_MINIO.String()}, minioFileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) - c.So(len(rsp), c.ShouldEqual, 1) + c.So(len(rsp), c.ShouldBeGreaterThanOrEqualTo, 1) }) c.Convey("test list fail", t, func() { - _, err := fileBiz.List(context.Background(), &api.ListFilesRequest{Bucket: minioBucket, Page: -1, PageSize: -1}, minioFileAuthor) + _, err := fileBiz.List(context.Background(), &api.ListFilesRequest{Bucket: minioBucket, Page: -1, PageSize: -1,Engine: api.FileEngine_FILE_ENGINE_MINIO.String()}, minioFileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "SQL syntax") }) @@ -456,6 +494,7 @@ func TestMinio(t *testing.T) { t.Run("testMinioMkBucket", testMinioMkBucket) t.Run("testMinioUpload", testMinioUpload) t.Run("testMinioDownload", testMinioDownload) + t.Run("testFileUpdated", testFileUpdated) t.Run("testMinioMeta", testMinioMeta) t.Run("testMinioVersion", testMinioVersion) t.Run("testMinioInitPartsUpload", testMinioInitPartsUpload) diff --git a/internal/data/file.go b/internal/data/file.go index f26f184..b40be99 100644 --- a/internal/data/file.go +++ b/internal/data/file.go @@ -3,6 +3,7 @@ package data import ( "context" "fmt" + "log" "github.com/begonia-org/begonia/internal/biz" "github.com/begonia-org/begonia/internal/biz/file" @@ -21,7 +22,7 @@ func NewFileRepoImpl(data *Data, curd biz.CURD) file.FileRepo { return &fileRepoImpl{data: data, curd: curd} } -func (f *fileRepoImpl) UpsertFile(ctx context.Context, in *api.Files) error { +func (f *fileRepoImpl) UpsertFile(ctx context.Context, in *api.Files) (bool, error) { in.UpdatedAt = timestamppb.Now() mask := make([]string, 0) if in.UpdateMask != nil { @@ -34,7 +35,7 @@ func (f *fileRepoImpl) DelFile(ctx context.Context, engine, bucket, key string) // return f.curd.Del(ctx, &api.Files{Uid: fid},false) return f.data.db.UpdateSelectColumns(ctx, &api.Files{Engine: engine, Bucket: bucket, Key: key}, &api.Files{IsDeleted: true}, "is_deleted") } -func (f *fileRepoImpl) UpsertBucket(ctx context.Context, bucket *api.Buckets) error { +func (f *fileRepoImpl) UpsertBucket(ctx context.Context, bucket *api.Buckets) (bool, error) { bucket.UpdatedAt = timestamppb.Now() mask := make([]string, 0) if bucket.UpdateMask != nil { @@ -68,16 +69,20 @@ func (f *fileRepoImpl) List(ctx context.Context, page, pageSize int32, bucket, e PageSize: pageSize, Args: make([]interface{}, 0), } - pagination.Query = "owner = ?" - pagination.Args = append(pagination.Args, owner) + pagination.Query = "" if bucket != "" { - pagination.Query = fmt.Sprintf("%s and bucket = ?", pagination.Query) + pagination.Query = "bucket = ?" pagination.Args = append(pagination.Args, bucket) } if engine != "" { - pagination.Query = fmt.Sprintf("%s and fs_engine = ?", pagination.Query) + if pagination.Query != "" { + pagination.Query = fmt.Sprintf("%s and fs_engine = ?", pagination.Query) + } else { + pagination.Query = "fs_engine = ?" + } pagination.Args = append(pagination.Args, engine) } + log.Printf("query:%s,args:%s", pagination.Query, pagination.Args) err := f.curd.List(ctx, &files, pagination) if err != nil { diff --git a/internal/data/file_test.go b/internal/data/file_test.go index 5516ba4..15e8a23 100644 --- a/internal/data/file_test.go +++ b/internal/data/file_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "testing" + "time" "github.com/agiledragon/gomonkey/v2" "github.com/begonia-org/begonia" @@ -17,7 +18,8 @@ import ( ) var fileBucketId = "" -var fileFileId="" +var fileFileId = "" + func testUpsertFile(t *testing.T) { c.Convey("Test UpsertFile", t, func() { env := "dev" @@ -28,10 +30,12 @@ func testUpsertFile(t *testing.T) { f := NewFileRepo(conf, gateway.Log) snk, _ := tiga.NewSnowflake(1) fileFileId = snk.GenerateIDString() + fk := fmt.Sprintf("test-%s", fileFileId) + // log.Printf("fileFileId:%s,file id %s",fileFileId,fk) file := &api.Files{ - Engine: "test", + Engine: fk, Bucket: "test", - Key: "test", + Key: fk, Uid: fileFileId, Owner: "test", CreatedAt: timestamppb.Now(), @@ -39,39 +43,42 @@ func testUpsertFile(t *testing.T) { UpdateMask: &fieldmaskpb.FieldMask{}, } // t.Logf("fileFileId:%s,file id %s",fileFileId,file.Uid) - err := f.UpsertFile(context.Background(), file) + updated, err := f.UpsertFile(context.Background(), file) c.So(err, c.ShouldBeNil) - + c.So(updated, c.ShouldBeFalse) + time.Sleep(3 * time.Second) + file.UpdatedAt = timestamppb.Now() file.UpdateMask.Paths = append(file.UpdateMask.Paths, "updated_at") - err = f.UpsertFile(context.Background(), file) + updated, err = f.UpsertFile(context.Background(), file) c.So(err, c.ShouldBeNil) + c.So(updated, c.ShouldBeTrue) }) } func testGetFileById(t *testing.T) { - c.Convey("Test GetFileById", t, func(){ + c.Convey("Test GetFileById", t, func() { env := "dev" if begonia.Env != "" { env = begonia.Env } conf := cfg.ReadConfig(env) f := NewFileRepo(conf, gateway.Log) - file,err := f.GetFileById(context.Background(),fileFileId) - c.So(err,c.ShouldBeNil) - c.So(file,c.ShouldNotBeNil) - - file,err=f.GetFile(context.Background(),"test","test","test") - c.So(err,c.ShouldBeNil) - c.So(file,c.ShouldNotBeNil) - patch:=gomonkey.ApplyFuncReturn(tiga.MySQLDao.First,fmt.Errorf("get error")) + file, err := f.GetFileById(context.Background(), fileFileId) + c.So(err, c.ShouldBeNil) + c.So(file, c.ShouldNotBeNil) + fk:=fmt.Sprintf("test-%s",fileFileId) + file, err = f.GetFile(context.Background(), fk, "test", fk) + c.So(err, c.ShouldBeNil) + c.So(file, c.ShouldNotBeNil) + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("get error")) defer patch.Reset() - _,err = f.GetFileById(context.Background(),"") + _, err = f.GetFileById(context.Background(), "") - c.So(err,c.ShouldNotBeNil) - c.So(err.Error(),c.ShouldContainSubstring,"get error") - _,err=f.GetFile(context.Background(),"test","test","test") - c.So(err,c.ShouldNotBeNil) - c.So(err.Error(),c.ShouldContainSubstring,"get error") + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get error") + _, err = f.GetFile(context.Background(), "test", "test", "test") + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get error") }) } @@ -84,9 +91,11 @@ func testUpsertBucket(t *testing.T) { conf := cfg.ReadConfig(env) f := NewFileRepo(conf, gateway.Log) snk, _ := tiga.NewSnowflake(1) + bk:=fmt.Sprintf("test-%s",snk.GenerateIDString()) + bucket := &api.Buckets{ Engine: "test", - Bucket: "test", + Bucket: bk, Uid: snk.GenerateIDString(), Owner: "test", CreatedAt: timestamppb.Now(), @@ -94,11 +103,15 @@ func testUpsertBucket(t *testing.T) { UpdateMask: &fieldmaskpb.FieldMask{}, } fileBucketId = bucket.Uid - err := f.UpsertBucket(context.Background(), bucket) + updated, err := f.UpsertBucket(context.Background(), bucket) c.So(err, c.ShouldBeNil) + c.So(updated, c.ShouldBeFalse) + time.Sleep(3 * time.Second) + bucket.UpdatedAt = timestamppb.Now() bucket.UpdateMask.Paths = append(bucket.UpdateMask.Paths, "updated_at") - err = f.UpsertBucket(context.Background(), bucket) + updated, err = f.UpsertBucket(context.Background(), bucket) c.So(err, c.ShouldBeNil) + c.So(updated, c.ShouldBeTrue) }) } func testFileList(t *testing.T) { @@ -112,6 +125,10 @@ func testFileList(t *testing.T) { files, err := f.List(context.Background(), 1, 10, "test", "test", "test") c.So(err, c.ShouldBeNil) c.So(files, c.ShouldNotBeNil) + + files, err = f.List(context.Background(), 1, 10, "", "test", "test") + c.So(err, c.ShouldBeNil) + c.So(files, c.ShouldNotBeNil) }) c.Convey("Test file List fail", t, func() { env := "dev" diff --git a/internal/service/file.go b/internal/service/file.go index 652b66f..bc11763 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -125,7 +125,7 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - log.Printf("download file author:%v", identity) + // log.Printf("download file author:%v", identity) newKey, err := url.PathUnescape(in.Key) if err != nil { return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "url_unescape") From 396bd7d3affb17ead86daec92e6c819dad1bae47 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Fri, 28 Jun 2024 17:01:06 +0800 Subject: [PATCH 16/16] feat:update tiga --- go.mod | 4 ++-- go.sum | 21 ++++----------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 9f640da..8517228 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/cockroachdb/errors v1.11.1 github.com/google/wire v0.6.0 github.com/smartystreets/goconvey v1.8.1 - github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac + github.com/spark-lence/tiga v0.0.0-20240628071333-f5e34bac7593 github.com/spf13/cobra v1.8.0 google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d google.golang.org/grpc v1.64.0 @@ -66,7 +66,7 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect - go.mongodb.org/mongo-driver v1.15.1 // indirect + go.mongodb.org/mongo-driver v1.16.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.24.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 diff --git a/go.sum b/go.sum index 3204baa..db5dd88 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoa github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -23,8 +22,6 @@ github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa h1:DH github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa/go.mod h1:xEqoca1vNGqH8CV7X9EzhDV5Ihtq9J95p7ZipzUB6pc= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw7T+90KC5QW7yCa8Nn/5psnlvsexipAOrQAcc7YE0= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= -github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f h1:1roWosaPjA7vE5CnnF6irDWt05SLD57bsHLun4HYeLU= -github.com/begonia-org/go-sdk v0.0.0-20240617104023-3182694e4a5f/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/begonia-org/go-sdk v0.0.0-20240628071225-2864d45934ea h1:jdDBLZVsGKfmF/V+U4WUrz4Hzd3/62GqMIly9gnSppw= github.com/begonia-org/go-sdk v0.0.0-20240628071225-2864d45934ea/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= @@ -121,7 +118,6 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -165,7 +161,6 @@ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -207,7 +202,6 @@ github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= @@ -235,10 +229,8 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4 h1:HmuGbn+mhKpi5tM8iM5vpUUx4K+XZZdrPecNu8IeO8c= -github.com/spark-lence/tiga v0.0.0-20240617015203-a7e022d3fac4/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= -github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac h1:qGLujl2jYtkIeQYa2JEzQIQ5W3grnNcqVc2uuPV3uco= -github.com/spark-lence/tiga v0.0.0-20240617161215-fd493c5e06ac/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= +github.com/spark-lence/tiga v0.0.0-20240628071333-f5e34bac7593 h1:wbjjiTqJWiAIQemNAE1Nw71roZQ8sshGJ70XHwBx8b4= +github.com/spark-lence/tiga v0.0.0-20240628071333-f5e34bac7593/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= @@ -287,8 +279,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxv go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= -go.mongodb.org/mongo-driver v1.15.1 h1:l+RvoUOoMXFmADTLfYDm7On9dRm7p4T80/lEQM+r7HU= -go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4= +go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -401,12 +393,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= -google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d h1:Aqf0fiIdUQEj0Gn9mKFFXoQfTTEaNopWpfVyYADxiSg= google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Od4k8V1LQSizPRUK4OzZ7TBE/20k+jPczUDAEyvn69Y= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:k3zyW3BYYR30e8v3x0bTDdE9vpYFjZHK+HcyqkrppWk= google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= @@ -418,7 +406,6 @@ gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UD gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

YHmOr~tdXtKI6AbkT3;rlY50F9h(n956vXpa&G{QOs(#ov~}E2JQWwo#fMUtmA)H zYn*?u>dpAbRCXI$vYhd&($yAe}B`ic8hY-&i3eJd3lc6D!^!<+ξztyw; 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 LAT@F?T)Fj6S>0xQ literal 283557 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 I4*D}d2CL%fihEAy;tq1Qth)(gY@W1C5es# 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⁡a`vPIg-J(3FVPV;4+%5jF(v}N5XqBp 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%0AWM+n{BL_OchkZpEcT2_ZxE9o>uncv0+r~ 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 J?(3g8^Iy~0vZDY1 From aea0e3bf9beb2b52bbcebf609a4c08833a5c3b95 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Wed, 12 Jun 2024 17:38:19 +0800 Subject: [PATCH 03/16] chore:add Architecture image --- .github/workflows/go.yml | 6 +- README.md | 6 +- README_ZH.md | 6 +- cmd/begonia/endpoint.go | 17 + cmd/begonia/main.go | 81 +- config/config.go | 7 + docs/doc_zh.md | 221 +++ docs/{overview.md => tutorials_zh.md} | 0 internal/biz/data_test.go | 11 +- internal/biz/endpoint/endpoint.go | 5 + internal/biz/endpoint/endpoint_test.go | 23 +- internal/data/app_test.go | 2 +- internal/data/data_test.go | 11 +- internal/data/endpoint.go | 41 +- internal/data/endpoint_test.go | 82 +- internal/pkg/config/config.go | 12 +- internal/pkg/config/config_test.go | 7 +- internal/service/base_test.go | 60 + internal/service/gateway_test.go | 2 +- test.log | 2353 +++++++++++++++++++++++- 20 files changed, 2896 insertions(+), 57 deletions(-) create mode 100644 docs/doc_zh.md rename docs/{overview.md => tutorials_zh.md} (100%) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4acd253..091661f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -3,10 +3,10 @@ name: Go on: push: paths: - - '**.go' + - "**.go" pull_request: paths: - - '**.go' + - "**.go" jobs: test: @@ -45,7 +45,7 @@ jobs: cd grpc-gateway go install ./protoc-gen-grpc-gateway - name: Test - run: go test -v -gcflags=-l -coverprofile=coverage.txt -covermode=atomic -ldflags -X=github.com/begonia-org/begonia.Version=$(git describe --tags --abbrev=0)\ -X=github.com/begonia-org/begonia.BuildTime=$(date '+%Y%m%d%H%M%S')\ -X=github.com/begonia-org/begonia.Commit=$(git rev-parse --short HEAD)\ -X=github.com/begonia-org/begonia.Env=test ./... + run: go test -p 1 -v -gcflags=-l -coverprofile=coverage.txt -covermode=atomic -ldflags -X=github.com/begonia-org/begonia.Version=$(git describe --tags --abbrev=0)\ -X=github.com/begonia-org/begonia.BuildTime=$(date '+%Y%m%d%H%M%S')\ -X=github.com/begonia-org/begonia.Commit=$(git rev-parse --short HEAD)\ -X=github.com/begonia-org/begonia.Env=test ./... - name: Upload coverage to Codecov uses: codecov/codecov-action@v4.0.1 diff --git a/README.md b/README.md index 747839f..1fb1b54 100644 --- a/README.md +++ b/README.md @@ -63,19 +63,19 @@ docker compose up -d #### 2. Initialize the database ```bash -begonia init -e dev +begonia init -e dev -c config/settings.yml ``` #### 3. Start the service ```bash -begonia start -e dev +begonia start -e dev -c config/settings.yml ``` #### 4. Register the service ```bash -go run . endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 +begonia endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 ``` #### 5. Test request service diff --git a/README_ZH.md b/README_ZH.md index 258221b..51c2a3f 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -65,19 +65,19 @@ docker compose up -d #### 2、初始化数据库 ```bash -begonia init -e dev +begonia init -e dev -c config/settings.yml ``` #### 3、启动服务 ```bash -begonia start -e dev +begonia start -e dev -c config/settings.yml ``` #### 4、注册服务 ```bash -go run . endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 +begonia endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 ``` #### 5、测试请求服务 diff --git a/cmd/begonia/endpoint.go b/cmd/begonia/endpoint.go index f314d15..f8764c0 100644 --- a/cmd/begonia/endpoint.go +++ b/cmd/begonia/endpoint.go @@ -102,6 +102,23 @@ func RegisterEndpoint(name string, endpoints []string, pbFile string, opts ...cl log.Printf("#####################Add Endpoint Success#####################") log.Printf("#####################ID:%s####################################", rsp.Id) } +func UpdateEndpoint(id string, mask []string, opts ...client.EndpointOption) { + readInitAPP() + apiClient := client.NewEndpointAPI(addr, accessKey, secret) + log.Printf("#####################Update Endpoint###########################") + patch := &endpoint.EndpointSrvUpdateRequest{} + patch.UniqueKey = id + for _, opt := range opts { + opt(patch) + } + _, err := apiClient.PatchEndpointConfig(context.Background(), patch) + if err != nil { + log.Fatalf(err.Error()) + panic(err.Error()) + } + log.Printf("#####################Update Endpoint %s Success#####################", id) + +} func DeleteEndpoint(id string) { readInitAPP() apiClient := client.NewEndpointAPI(addr, accessKey, secret) diff --git a/cmd/begonia/main.go b/cmd/begonia/main.go index ec3a7fb..d136ed4 100644 --- a/cmd/begonia/main.go +++ b/cmd/begonia/main.go @@ -10,6 +10,7 @@ import ( "github.com/begonia-org/begonia/config" "github.com/begonia-org/begonia/gateway" "github.com/begonia-org/begonia/internal" + api "github.com/begonia-org/go-sdk/api/endpoint/v1" "github.com/begonia-org/go-sdk/client" "github.com/spf13/cobra" ) @@ -17,6 +18,8 @@ import ( // var ProviderSet = wire.NewSet(NewMasterCmd) func addCommonCommand(cmd *cobra.Command) *cobra.Command { cmd.Flags().StringP("env", "e", "dev", "Runtime Environment") + cmd.Flags().StringP("config", "c", "./config/settings.yml", "Config file path") + _ = cmd.MarkFlagRequired("config") return cmd } func NewInitCmd() *cobra.Command { @@ -25,10 +28,14 @@ func NewInitCmd() *cobra.Command { Short: "Init Database", Run: func(cmd *cobra.Command, args []string) { env, _ := cmd.Flags().GetString("env") - config := config.ReadConfig(env) + cnf, err := cmd.Flags().GetString("config") + if err != nil { + log.Fatalf("failed to get config: %v", err) + } + config := config.ReadConfigWithDir(env, cnf) operator := internal.InitOperatorApp(config) log.Printf("init database") - err := operator.Init() + err = operator.Init() if err != nil { log.Fatalf("failed to init database: %v", err) } @@ -45,7 +52,11 @@ func NewGatewayCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { endpoint, _ := cmd.Flags().GetString("endpoint") env, _ := cmd.Flags().GetString("env") - config := config.ReadConfig(env) + cnf, err := cmd.Flags().GetString("config") + if err != nil { + log.Fatalf("failed to get config: %v", err) + } + config := config.ReadConfigWithDir(env, cnf) worker := internal.New(config, gateway.Log, endpoint) hd, _ := os.UserHomeDir() _ = os.WriteFile(hd+"/.begonia/gateway.json", []byte(fmt.Sprintf(`{"addr":"http://%s"}`, endpoint)), 0666) @@ -89,14 +100,69 @@ func NewEndpointAddCmd() *cobra.Command { RegisterEndpoint(name, endpoints, desc, client.WithBalance(strings.ToUpper(balance)), client.WithTags(tags)) }, } + cmd = newWriteEndpointCmd(cmd) + _ = cmd.MarkFlagRequired("name") + _ = cmd.MarkFlagRequired("endpoint") + _ = cmd.MarkFlagRequired("desc") + return cmd +} +func newWriteEndpointCmd(cmd *cobra.Command) *cobra.Command { cmd.Flags().StringArrayP("endpoint", "p", []string{}, "Endpoint Of Your Service (example:127.0.0.1:1949)") cmd.Flags().StringP("name", "n", "", "Service Name") cmd.Flags().StringP("desc", "d", "", "Descriptions Set Of Your Service (example:./example/example.pb)") cmd.Flags().StringArrayP("tags", "t", []string{}, "Tags Of Your Service") cmd.Flags().StringP("balance", "b", "RR", "Balance Type Of Your Service (options: RR WRR LC WLC CH SED NQ)") - _ = cmd.MarkFlagRequired("name") - _ = cmd.MarkFlagRequired("endpoint") - _ = cmd.MarkFlagRequired("desc") + + return cmd +} +func NewEndpointUpdateCmd() *cobra.Command { + var cmd = &cobra.Command{ + Use: "update", + Short: "Update Service To Gateway", + + Run: func(cmd *cobra.Command, args []string) { + id, _ := cmd.Flags().GetString("id") + name, _ := cmd.Flags().GetString("name") + desc, _ := cmd.Flags().GetString("desc") + tags, _ := cmd.Flags().GetStringArray("tags") + balance, _ := cmd.Flags().GetString("balance") + endpoints, _ := cmd.Flags().GetStringArray("endpoint") + mask := make([]string, 0) + options := make([]client.EndpointOption, 0) + if cmd.Flags().Changed("name") { + options = append(options, client.WithName(name)) + mask = append(mask, "name") + } + + if cmd.Flags().Changed("desc") { + options = append(options, client.WithDescription(desc)) + mask = append(mask, "description") + } + if cmd.Flags().Changed("tags") { + options = append(options, client.WithTags(tags)) + mask = append(mask, "tags") + } + if cmd.Flags().Changed("balance") { + options = append(options, client.WithBalance(strings.ToUpper(balance))) + mask = append(mask, "balance") + } + if cmd.Flags().Changed("endpoints") { + meta := make([]*api.EndpointMeta, 0) + for i, v := range endpoints { + meta = append(meta, &api.EndpointMeta{ + Addr: v, + Weight: int32(i), + }) + } + options = append(options, client.WithEndpoints(meta)) + mask = append(mask, "endpoints") + } + UpdateEndpoint(id, mask, options...) + }, + } + cmd = newWriteEndpointCmd(cmd) + cmd.Flags().StringP("id", "i", "", "ID Of Your Service") + _ = cmd.MarkFlagRequired("id") return cmd } func NewEndpointCmd() *cobra.Command { @@ -109,6 +175,7 @@ func NewEndpointCmd() *cobra.Command { cmd.AddCommand(NewEndpointAddCmd()) cmd.AddCommand(NewEndpointDelCmd()) + cmd.AddCommand(NewEndpointUpdateCmd()) return cmd } func NewBegoniaInfoCmd() *cobra.Command { @@ -134,6 +201,6 @@ func main() { rootCmd.AddCommand(addCommonCommand(NewInitCmd())) rootCmd.AddCommand(NewEndpointCmd()) if err := cmd.Execute(); err != nil { - log.Fatalf("failed to start master: %v", err) + log.Fatalf("failed to start begonia: %v", err) } } diff --git a/config/config.go b/config/config.go index 4cc94a0..c137497 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "log" "path/filepath" "runtime" @@ -10,5 +11,11 @@ import ( func ReadConfig(env string) *tiga.Configuration { _, filename, _, _ := runtime.Caller(0) dir := filepath.Dir(filename) + log.Printf("config dir: %s", dir) + return tiga.InitSettings(env, dir) +} +func ReadConfigWithDir(env, filename string) *tiga.Configuration { + dir := filepath.Dir(filename) + log.Printf("config dir: %s", dir) return tiga.InitSettings(env, dir) } diff --git a/docs/doc_zh.md b/docs/doc_zh.md new file mode 100644 index 0000000..efade9a --- /dev/null +++ b/docs/doc_zh.md @@ -0,0 +1,221 @@ +# 架构 + +Begonia 是一个 HTTP 反向代理服务器,支持基于 http1.1 协议对各类 gRPC 服务进行代理转发。 +![架构](./begonia.png) + +## 核心模块 + +### 1、流量转发 + +- 该模块的核心功能是基于[gRPC-gateway](https://github.com/grpc-ecosystem/grpc-gateway)对 http 路由进行管理和维护。 +- Begonia 根据请求头携带的 content-type 选择对应的序列化器将 http 请求数据和请求参数序列化为 protobuf 数据并转发到下游的 gRPC service。 +- 基于 gRPC 的`grpc.UnknownServiceHandler`实现对 gRPC 流量的转发。 +- 同样的,begonia 会根据 Accept 将 gRPC 服务返回的数据序列化为对应的数据格式 + +### 2、gRPC 中间件 + +begonia 提供了两类中间件: + +- 访问控制中间件,包含了 apikey、AKSK 及 JWT 身份认证方式,用于对于请求进行身份认证 +- 请求处理中间件,包含 RequestId 生成、参数校验、异常捕获及响应体格式修饰模块 + +### 3、服务注册和发现 + +Begonia 通过`protoc --descriptor_set_out`生成的`FileDescriptorSet`二进制文件对于服务进行解析、更新和维护,在注册服务时需要按照下面的数据格式提交到 ETCD: + +```json +{ + "descriptor_set": "", + "name": "test", + "service_name": "test", + "description": "test", + "endpoints": [ + { + "addr": "127.0.0.1:21213", + "weight": 0 + }, + { + "addr": "127.0.0.1:21214", + "weight": 0 + }, + { + "addr": "127.0.0.1:21215", + "weight": 0 + } + ], + "balance": "RR", + "tags": ["test"] +} +``` +# 初始化 +- Begonia 服务的初始化主要内容包括: + + - MySQL创建数据表 + + - 生成admin用户账号和密码 + + - 生成admin app AccessKey和SecretKey +- 初始化命令如下: +```bash +begonia init -e dev -c config/settings.yml +``` + - 参数`-e`指定环境,可选值为`dev`、`test`、`prod`,默认为`prod` + - 参数`-c`指定配置文件路径,默认为`config/settings.yml` +- 默认admin用户账号和密码为`admin`和`admin`,可以在配置文件中修改 +- 初始化生成admin app的AccessKey和SecretKey,在`~/.begonia/admin-app.json`中保存 + + +# 添加Endpoint +Endpoint是指业务的应用服务的配置,包含应用的地址和gRPC服务描述服的集合,一个完整的Endpoint对象如下: +```json +{ + "descriptor_set": "CqwFChVnb29nbGUvYXBpL2h0dHAucHJvdG8SCmdvb2dsZS5hcGkieQoESHR0cBIqCgVydWxlcxgBIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVSBXJ1bGVzEkUKH2Z1bGx5X2RlY29kZV9yZXNlcnZlZF9leHBhbnNpb24YAiABKAhSHGZ1bGx5RGVjb2RlUmVzZXJ2ZWRFeHBhbnNpb24i2gIKCEh0dHBSdWxlEhoKCHNlbGVjdG9yGAEgASgJUghzZWxlY3RvchISCgNnZXQYAiABKAlIAFIDZ2V0EhIKA3B1dBgDIAEoCUgAUgNwdXQSFAoEcG9zdBgEIAEoCUgAUgRwb3N0EhgKBmRlbGV0ZRgFIAEoCUgAUgZkZWxldGUSFgoFcGF0Y2gYBiABKAlIAFIFcGF0Y2gSNwoGY3VzdG9tGAggASgLMh0uZ29vZ2xlLmFwaS5DdXN0b21IdHRwUGF0dGVybkgAUgZjdXN0b20SEgoEYm9keRgHIAEoCVIEYm9keRIjCg1yZXNwb25zZV9ib2R5GAwgASgJUgxyZXNwb25zZUJvZHkSRQoTYWRkaXRpb25hbF9iaW5kaW5ncxgLIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVSEmFkZGl0aW9uYWxCaW5kaW5nc0IJCgdwYXR0ZXJuIjsKEUN1c3RvbUh0dHBQYXR0ZXJuEhIKBGtpbmQYASABKAlSBGtpbmQSEgoEcGF0aBgCIAEoCVIEcGF0aEJqCg5jb20uZ29vZ2xlLmFwaUIJSHR0cFByb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25z+AEBogIER0FQSWIGcHJvdG8zCuFCCiBnb29nbGUvcHJvdG9idWYvZGVzY3JpcHRvci5wcm90bxIPZ29vZ2xlLnByb3RvYnVmIk0KEUZpbGVEZXNjcmlwdG9yU2V0EjgKBGZpbGUYASADKAsyJC5nb29nbGUucHJvdG9idWYuRmlsZURlc2NyaXB0b3JQcm90b1IEZmlsZSL+BAoTRmlsZURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEhgKB3BhY2thZ2UYAiABKAlSB3BhY2thZ2USHgoKZGVwZW5kZW5jeRgDIAMoCVIKZGVwZW5kZW5jeRIrChFwdWJsaWNfZGVwZW5kZW5jeRgKIAMoBVIQcHVibGljRGVwZW5kZW5jeRInCg93ZWFrX2RlcGVuZGVuY3kYCyADKAVSDndlYWtEZXBlbmRlbmN5EkMKDG1lc3NhZ2VfdHlwZRgEIAMoCzIgLmdvb2dsZS5wcm90b2J1Zi5EZXNjcmlwdG9yUHJvdG9SC21lc3NhZ2VUeXBlEkEKCWVudW1fdHlwZRgFIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5FbnVtRGVzY3JpcHRvclByb3RvUghlbnVtVHlwZRJBCgdzZXJ2aWNlGAYgAygLMicuZ29vZ2xlLnByb3RvYnVmLlNlcnZpY2VEZXNjcmlwdG9yUHJvdG9SB3NlcnZpY2USQwoJZXh0ZW5zaW9uGAcgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvUglleHRlbnNpb24SNgoHb3B0aW9ucxgIIAEoCzIcLmdvb2dsZS5wcm90b2J1Zi5GaWxlT3B0aW9uc1IHb3B0aW9ucxJJChBzb3VyY2VfY29kZV9pbmZvGAkgASgLMh8uZ29vZ2xlLnByb3RvYnVmLlNvdXJjZUNvZGVJbmZvUg5zb3VyY2VDb2RlSW5mbxIWCgZzeW50YXgYDCABKAlSBnN5bnRheBIYCgdlZGl0aW9uGA0gASgJUgdlZGl0aW9uIrkGCg9EZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRI7CgVmaWVsZBgCIAMoCzIlLmdvb2dsZS5wcm90b2J1Zi5GaWVsZERlc2NyaXB0b3JQcm90b1IFZmllbGQSQwoJZXh0ZW5zaW9uGAYgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvUglleHRlbnNpb24SQQoLbmVzdGVkX3R5cGUYAyADKAsyIC5nb29nbGUucHJvdG9idWYuRGVzY3JpcHRvclByb3RvUgpuZXN0ZWRUeXBlEkEKCWVudW1fdHlwZRgEIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5FbnVtRGVzY3JpcHRvclByb3RvUghlbnVtVHlwZRJYCg9leHRlbnNpb25fcmFuZ2UYBSADKAsyLy5nb29nbGUucHJvdG9idWYuRGVzY3JpcHRvclByb3RvLkV4dGVuc2lvblJhbmdlUg5leHRlbnNpb25SYW5nZRJECgpvbmVvZl9kZWNsGAggAygLMiUuZ29vZ2xlLnByb3RvYnVmLk9uZW9mRGVzY3JpcHRvclByb3RvUglvbmVvZkRlY2wSOQoHb3B0aW9ucxgHIAEoCzIfLmdvb2dsZS5wcm90b2J1Zi5NZXNzYWdlT3B0aW9uc1IHb3B0aW9ucxJVCg5yZXNlcnZlZF9yYW5nZRgJIAMoCzIuLmdvb2dsZS5wcm90b2J1Zi5EZXNjcmlwdG9yUHJvdG8uUmVzZXJ2ZWRSYW5nZVINcmVzZXJ2ZWRSYW5nZRIjCg1yZXNlcnZlZF9uYW1lGAogAygJUgxyZXNlcnZlZE5hbWUaegoORXh0ZW5zaW9uUmFuZ2USFAoFc3RhcnQYASABKAVSBXN0YXJ0EhAKA2VuZBgCIAEoBVIDZW5kEkAKB29wdGlvbnMYAyABKAsyJi5nb29nbGUucHJvdG9idWYuRXh0ZW5zaW9uUmFuZ2VPcHRpb25zUgdvcHRpb25zGjcKDVJlc2VydmVkUmFuZ2USFAoFc3RhcnQYASABKAVSBXN0YXJ0EhAKA2VuZBgCIAEoBVIDZW5kInwKFUV4dGVuc2lvblJhbmdlT3B0aW9ucxJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACIsEGChRGaWVsZERlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEhYKBm51bWJlchgDIAEoBVIGbnVtYmVyEkEKBWxhYmVsGAQgASgOMisuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvLkxhYmVsUgVsYWJlbBI+CgR0eXBlGAUgASgOMiouZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvLlR5cGVSBHR5cGUSGwoJdHlwZV9uYW1lGAYgASgJUgh0eXBlTmFtZRIaCghleHRlbmRlZRgCIAEoCVIIZXh0ZW5kZWUSIwoNZGVmYXVsdF92YWx1ZRgHIAEoCVIMZGVmYXVsdFZhbHVlEh8KC29uZW9mX2luZGV4GAkgASgFUgpvbmVvZkluZGV4EhsKCWpzb25fbmFtZRgKIAEoCVIIanNvbk5hbWUSNwoHb3B0aW9ucxgIIAEoCzIdLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnNSB29wdGlvbnMSJwoPcHJvdG8zX29wdGlvbmFsGBEgASgIUg5wcm90bzNPcHRpb25hbCK2AgoEVHlwZRIPCgtUWVBFX0RPVUJMRRABEg4KClRZUEVfRkxPQVQQAhIOCgpUWVBFX0lOVDY0EAMSDwoLVFlQRV9VSU5UNjQQBBIOCgpUWVBFX0lOVDMyEAUSEAoMVFlQRV9GSVhFRDY0EAYSEAoMVFlQRV9GSVhFRDMyEAcSDQoJVFlQRV9CT09MEAgSDwoLVFlQRV9TVFJJTkcQCRIOCgpUWVBFX0dST1VQEAoSEAoMVFlQRV9NRVNTQUdFEAsSDgoKVFlQRV9CWVRFUxAMEg8KC1RZUEVfVUlOVDMyEA0SDQoJVFlQRV9FTlVNEA4SEQoNVFlQRV9TRklYRUQzMhAPEhEKDVRZUEVfU0ZJWEVENjQQEBIPCgtUWVBFX1NJTlQzMhAREg8KC1RZUEVfU0lOVDY0EBIiQwoFTGFiZWwSEgoOTEFCRUxfT1BUSU9OQUwQARISCg5MQUJFTF9SRVFVSVJFRBACEhIKDkxBQkVMX1JFUEVBVEVEEAMiYwoUT25lb2ZEZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRI3CgdvcHRpb25zGAIgASgLMh0uZ29vZ2xlLnByb3RvYnVmLk9uZW9mT3B0aW9uc1IHb3B0aW9ucyLjAgoTRW51bURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEj8KBXZhbHVlGAIgAygLMikuZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1ZURlc2NyaXB0b3JQcm90b1IFdmFsdWUSNgoHb3B0aW9ucxgDIAEoCzIcLmdvb2dsZS5wcm90b2J1Zi5FbnVtT3B0aW9uc1IHb3B0aW9ucxJdCg5yZXNlcnZlZF9yYW5nZRgEIAMoCzI2Lmdvb2dsZS5wcm90b2J1Zi5FbnVtRGVzY3JpcHRvclByb3RvLkVudW1SZXNlcnZlZFJhbmdlUg1yZXNlcnZlZFJhbmdlEiMKDXJlc2VydmVkX25hbWUYBSADKAlSDHJlc2VydmVkTmFtZRo7ChFFbnVtUmVzZXJ2ZWRSYW5nZRIUCgVzdGFydBgBIAEoBVIFc3RhcnQSEAoDZW5kGAIgASgFUgNlbmQigwEKGEVudW1WYWx1ZURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEhYKBm51bWJlchgCIAEoBVIGbnVtYmVyEjsKB29wdGlvbnMYAyABKAsyIS5nb29nbGUucHJvdG9idWYuRW51bVZhbHVlT3B0aW9uc1IHb3B0aW9ucyKnAQoWU2VydmljZURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEj4KBm1ldGhvZBgCIAMoCzImLmdvb2dsZS5wcm90b2J1Zi5NZXRob2REZXNjcmlwdG9yUHJvdG9SBm1ldGhvZBI5CgdvcHRpb25zGAMgASgLMh8uZ29vZ2xlLnByb3RvYnVmLlNlcnZpY2VPcHRpb25zUgdvcHRpb25zIokCChVNZXRob2REZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRIdCgppbnB1dF90eXBlGAIgASgJUglpbnB1dFR5cGUSHwoLb3V0cHV0X3R5cGUYAyABKAlSCm91dHB1dFR5cGUSOAoHb3B0aW9ucxgEIAEoCzIeLmdvb2dsZS5wcm90b2J1Zi5NZXRob2RPcHRpb25zUgdvcHRpb25zEjAKEGNsaWVudF9zdHJlYW1pbmcYBSABKAg6BWZhbHNlUg9jbGllbnRTdHJlYW1pbmcSMAoQc2VydmVyX3N0cmVhbWluZxgGIAEoCDoFZmFsc2VSD3NlcnZlclN0cmVhbWluZyKRCQoLRmlsZU9wdGlvbnMSIQoMamF2YV9wYWNrYWdlGAEgASgJUgtqYXZhUGFja2FnZRIwChRqYXZhX291dGVyX2NsYXNzbmFtZRgIIAEoCVISamF2YU91dGVyQ2xhc3NuYW1lEjUKE2phdmFfbXVsdGlwbGVfZmlsZXMYCiABKAg6BWZhbHNlUhFqYXZhTXVsdGlwbGVGaWxlcxJECh1qYXZhX2dlbmVyYXRlX2VxdWFsc19hbmRfaGFzaBgUIAEoCEICGAFSGWphdmFHZW5lcmF0ZUVxdWFsc0FuZEhhc2gSOgoWamF2YV9zdHJpbmdfY2hlY2tfdXRmOBgbIAEoCDoFZmFsc2VSE2phdmFTdHJpbmdDaGVja1V0ZjgSUwoMb3B0aW1pemVfZm9yGAkgASgOMikuZ29vZ2xlLnByb3RvYnVmLkZpbGVPcHRpb25zLk9wdGltaXplTW9kZToFU1BFRURSC29wdGltaXplRm9yEh0KCmdvX3BhY2thZ2UYCyABKAlSCWdvUGFja2FnZRI1ChNjY19nZW5lcmljX3NlcnZpY2VzGBAgASgIOgVmYWxzZVIRY2NHZW5lcmljU2VydmljZXMSOQoVamF2YV9nZW5lcmljX3NlcnZpY2VzGBEgASgIOgVmYWxzZVITamF2YUdlbmVyaWNTZXJ2aWNlcxI1ChNweV9nZW5lcmljX3NlcnZpY2VzGBIgASgIOgVmYWxzZVIRcHlHZW5lcmljU2VydmljZXMSNwoUcGhwX2dlbmVyaWNfc2VydmljZXMYKiABKAg6BWZhbHNlUhJwaHBHZW5lcmljU2VydmljZXMSJQoKZGVwcmVjYXRlZBgXIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSLgoQY2NfZW5hYmxlX2FyZW5hcxgfIAEoCDoEdHJ1ZVIOY2NFbmFibGVBcmVuYXMSKgoRb2JqY19jbGFzc19wcmVmaXgYJCABKAlSD29iamNDbGFzc1ByZWZpeBIpChBjc2hhcnBfbmFtZXNwYWNlGCUgASgJUg9jc2hhcnBOYW1lc3BhY2USIQoMc3dpZnRfcHJlZml4GCcgASgJUgtzd2lmdFByZWZpeBIoChBwaHBfY2xhc3NfcHJlZml4GCggASgJUg5waHBDbGFzc1ByZWZpeBIjCg1waHBfbmFtZXNwYWNlGCkgASgJUgxwaHBOYW1lc3BhY2USNAoWcGhwX21ldGFkYXRhX25hbWVzcGFjZRgsIAEoCVIUcGhwTWV0YWRhdGFOYW1lc3BhY2USIQoMcnVieV9wYWNrYWdlGC0gASgJUgtydWJ5UGFja2FnZRJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbiI6CgxPcHRpbWl6ZU1vZGUSCQoFU1BFRUQQARINCglDT0RFX1NJWkUQAhIQCgxMSVRFX1JVTlRJTUUQAyoJCOgHEICAgIACSgQIJhAnIrsDCg5NZXNzYWdlT3B0aW9ucxI8ChdtZXNzYWdlX3NldF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2VSFG1lc3NhZ2VTZXRXaXJlRm9ybWF0EkwKH25vX3N0YW5kYXJkX2Rlc2NyaXB0b3JfYWNjZXNzb3IYAiABKAg6BWZhbHNlUhxub1N0YW5kYXJkRGVzY3JpcHRvckFjY2Vzc29yEiUKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNlUgpkZXByZWNhdGVkEhsKCW1hcF9lbnRyeRgHIAEoCFIIbWFwRW50cnkSVgomZGVwcmVjYXRlZF9sZWdhY3lfanNvbl9maWVsZF9jb25mbGljdHMYCyABKAhCAhgBUiJkZXByZWNhdGVkTGVnYWN5SnNvbkZpZWxkQ29uZmxpY3RzElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgEEAVKBAgFEAZKBAgGEAdKBAgIEAlKBAgJEAoitwgKDEZpZWxkT3B0aW9ucxJBCgVjdHlwZRgBIAEoDjIjLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuQ1R5cGU6BlNUUklOR1IFY3R5cGUSFgoGcGFja2VkGAIgASgIUgZwYWNrZWQSRwoGanN0eXBlGAYgASgOMiQuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5KU1R5cGU6CUpTX05PUk1BTFIGanN0eXBlEhkKBGxhenkYBSABKAg6BWZhbHNlUgRsYXp5Ei4KD3VudmVyaWZpZWRfbGF6eRgPIAEoCDoFZmFsc2VSDnVudmVyaWZpZWRMYXp5EiUKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNlUgpkZXByZWNhdGVkEhkKBHdlYWsYCiABKAg6BWZhbHNlUgR3ZWFrEigKDGRlYnVnX3JlZGFjdBgQIAEoCDoFZmFsc2VSC2RlYnVnUmVkYWN0EksKCXJldGVudGlvbhgRIAEoDjItLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuT3B0aW9uUmV0ZW50aW9uUglyZXRlbnRpb24SRgoGdGFyZ2V0GBIgASgOMi4uZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5PcHRpb25UYXJnZXRUeXBlUgZ0YXJnZXQSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24iLwoFQ1R5cGUSCgoGU1RSSU5HEAASCAoEQ09SRBABEhAKDFNUUklOR19QSUVDRRACIjUKBkpTVHlwZRINCglKU19OT1JNQUwQABINCglKU19TVFJJTkcQARINCglKU19OVU1CRVIQAiJVCg9PcHRpb25SZXRlbnRpb24SFQoRUkVURU5USU9OX1VOS05PV04QABIVChFSRVRFTlRJT05fUlVOVElNRRABEhQKEFJFVEVOVElPTl9TT1VSQ0UQAiKMAgoQT3B0aW9uVGFyZ2V0VHlwZRIXChNUQVJHRVRfVFlQRV9VTktOT1dOEAASFAoQVEFSR0VUX1RZUEVfRklMRRABEh8KG1RBUkdFVF9UWVBFX0VYVEVOU0lPTl9SQU5HRRACEhcKE1RBUkdFVF9UWVBFX01FU1NBR0UQAxIVChFUQVJHRVRfVFlQRV9GSUVMRBAEEhUKEVRBUkdFVF9UWVBFX09ORU9GEAUSFAoQVEFSR0VUX1RZUEVfRU5VTRAGEhoKFlRBUkdFVF9UWVBFX0VOVU1fRU5UUlkQBxIXChNUQVJHRVRfVFlQRV9TRVJWSUNFEAgSFgoSVEFSR0VUX1RZUEVfTUVUSE9EEAkqCQjoBxCAgICAAkoECAQQBSJzCgxPbmVvZk9wdGlvbnMSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKYAgoLRW51bU9wdGlvbnMSHwoLYWxsb3dfYWxpYXMYAiABKAhSCmFsbG93QWxpYXMSJQoKZGVwcmVjYXRlZBgDIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSVgomZGVwcmVjYXRlZF9sZWdhY3lfanNvbl9maWVsZF9jb25mbGljdHMYBiABKAhCAhgBUiJkZXByZWNhdGVkTGVnYWN5SnNvbkZpZWxkQ29uZmxpY3RzElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgFEAYingEKEEVudW1WYWx1ZU9wdGlvbnMSJQoKZGVwcmVjYXRlZBgBIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKcAQoOU2VydmljZU9wdGlvbnMSJQoKZGVwcmVjYXRlZBghIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiLgAgoNTWV0aG9kT3B0aW9ucxIlCgpkZXByZWNhdGVkGCEgASgIOgVmYWxzZVIKZGVwcmVjYXRlZBJxChFpZGVtcG90ZW5jeV9sZXZlbBgiIAEoDjIvLmdvb2dsZS5wcm90b2J1Zi5NZXRob2RPcHRpb25zLklkZW1wb3RlbmN5TGV2ZWw6E0lERU1QT1RFTkNZX1VOS05PV05SEGlkZW1wb3RlbmN5TGV2ZWwSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24iUAoQSWRlbXBvdGVuY3lMZXZlbBIXChNJREVNUE9URU5DWV9VTktOT1dOEAASEwoPTk9fU0lERV9FRkZFQ1RTEAESDgoKSURFTVBPVEVOVBACKgkI6AcQgICAgAIimgMKE1VuaW50ZXJwcmV0ZWRPcHRpb24SQQoEbmFtZRgCIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uLk5hbWVQYXJ0UgRuYW1lEikKEGlkZW50aWZpZXJfdmFsdWUYAyABKAlSD2lkZW50aWZpZXJWYWx1ZRIsChJwb3NpdGl2ZV9pbnRfdmFsdWUYBCABKARSEHBvc2l0aXZlSW50VmFsdWUSLAoSbmVnYXRpdmVfaW50X3ZhbHVlGAUgASgDUhBuZWdhdGl2ZUludFZhbHVlEiEKDGRvdWJsZV92YWx1ZRgGIAEoAVILZG91YmxlVmFsdWUSIQoMc3RyaW5nX3ZhbHVlGAcgASgMUgtzdHJpbmdWYWx1ZRInCg9hZ2dyZWdhdGVfdmFsdWUYCCABKAlSDmFnZ3JlZ2F0ZVZhbHVlGkoKCE5hbWVQYXJ0EhsKCW5hbWVfcGFydBgBIAIoCVIIbmFtZVBhcnQSIQoMaXNfZXh0ZW5zaW9uGAIgAigIUgtpc0V4dGVuc2lvbiKnAgoOU291cmNlQ29kZUluZm8SRAoIbG9jYXRpb24YASADKAsyKC5nb29nbGUucHJvdG9idWYuU291cmNlQ29kZUluZm8uTG9jYXRpb25SCGxvY2F0aW9uGs4BCghMb2NhdGlvbhIWCgRwYXRoGAEgAygFQgIQAVIEcGF0aBIWCgRzcGFuGAIgAygFQgIQAVIEc3BhbhIpChBsZWFkaW5nX2NvbW1lbnRzGAMgASgJUg9sZWFkaW5nQ29tbWVudHMSKwoRdHJhaWxpbmdfY29tbWVudHMYBCABKAlSEHRyYWlsaW5nQ29tbWVudHMSOgoZbGVhZGluZ19kZXRhY2hlZF9jb21tZW50cxgGIAMoCVIXbGVhZGluZ0RldGFjaGVkQ29tbWVudHMi0AIKEUdlbmVyYXRlZENvZGVJbmZvEk0KCmFubm90YXRpb24YASADKAsyLS5nb29nbGUucHJvdG9idWYuR2VuZXJhdGVkQ29kZUluZm8uQW5ub3RhdGlvblIKYW5ub3RhdGlvbhrrAQoKQW5ub3RhdGlvbhIWCgRwYXRoGAEgAygFQgIQAVIEcGF0aBIfCgtzb3VyY2VfZmlsZRgCIAEoCVIKc291cmNlRmlsZRIUCgViZWdpbhgDIAEoBVIFYmVnaW4SEAoDZW5kGAQgASgFUgNlbmQSUgoIc2VtYW50aWMYBSABKA4yNi5nb29nbGUucHJvdG9idWYuR2VuZXJhdGVkQ29kZUluZm8uQW5ub3RhdGlvbi5TZW1hbnRpY1IIc2VtYW50aWMiKAoIU2VtYW50aWMSCAoETk9ORRAAEgcKA1NFVBABEgkKBUFMSUFTEAJCfgoTY29tLmdvb2dsZS5wcm90b2J1ZkIQRGVzY3JpcHRvclByb3Rvc0gBWi1nb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi90eXBlcy9kZXNjcmlwdG9ycGL4AQGiAgNHUEKqAhpHb29nbGUuUHJvdG9idWYuUmVmbGVjdGlvbgqoAgocZ29vZ2xlL2FwaS9hbm5vdGF0aW9ucy5wcm90bxIKZ29vZ2xlLmFwaRoVZ29vZ2xlL2FwaS9odHRwLnByb3RvGiBnb29nbGUvcHJvdG9idWYvZGVzY3JpcHRvci5wcm90bzpLCgRodHRwEh4uZ29vZ2xlLnByb3RvYnVmLk1ldGhvZE9wdGlvbnMYsMq8IiABKAsyFC5nb29nbGUuYXBpLkh0dHBSdWxlUgRodHRwQm4KDmNvbS5nb29nbGUuYXBpQhBBbm5vdGF0aW9uc1Byb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25zogIER0FQSWIGcHJvdG8zCuYBCiBnb29nbGUvcHJvdG9idWYvZmllbGRfbWFzay5wcm90bxIPZ29vZ2xlLnByb3RvYnVmIiEKCUZpZWxkTWFzaxIUCgVwYXRocxgBIAMoCVIFcGF0aHNChQEKE2NvbS5nb29nbGUucHJvdG9idWZCDkZpZWxkTWFza1Byb3RvUAFaMmdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3R5cGVzL2tub3duL2ZpZWxkbWFza3Bi+AEBogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90bzMK5AEKGWdvb2dsZS9wcm90b2J1Zi9hbnkucHJvdG8SD2dvb2dsZS5wcm90b2J1ZiI2CgNBbnkSGQoIdHlwZV91cmwYASABKAlSB3R5cGVVcmwSFAoFdmFsdWUYAiABKAxSBXZhbHVlQnYKE2NvbS5nb29nbGUucHJvdG9idWZCCEFueVByb3RvUAFaLGdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3R5cGVzL2tub3duL2FueXBiogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90bzMKrQIKGWdvb2dsZS9hcGkvaHR0cGJvZHkucHJvdG8SCmdvb2dsZS5hcGkaGWdvb2dsZS9wcm90b2J1Zi9hbnkucHJvdG8idwoISHR0cEJvZHkSIQoMY29udGVudF90eXBlGAEgASgJUgtjb250ZW50VHlwZRISCgRkYXRhGAIgASgMUgRkYXRhEjQKCmV4dGVuc2lvbnMYAyADKAsyFC5nb29nbGUucHJvdG9idWYuQW55UgpleHRlbnNpb25zQmgKDmNvbS5nb29nbGUuYXBpQg1IdHRwQm9keVByb3RvUAFaO2dvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2h0dHBib2R5O2h0dHBib2R5+AEBogIER0FQSWIGcHJvdG8zCrcMCg1leGFtcGxlLnByb3RvEgpoZWxsb3dvcmxkGhxnb29nbGUvYXBpL2Fubm90YXRpb25zLnByb3RvGiBnb29nbGUvcHJvdG9idWYvZmllbGRfbWFzay5wcm90bxogZ29vZ2xlL3Byb3RvYnVmL2Rlc2NyaXB0b3IucHJvdG8aGWdvb2dsZS9hcGkvaHR0cGJvZHkucHJvdG8iNAoMRXJyb3JSZXF1ZXN0EhAKA21zZxgBIAEoCVIDbXNnEhIKBGNvZGUYAiABKAVSBGNvZGUiNAoMSGVsbG9SZXF1ZXN0EhAKA21zZxgBIAEoCVIDbXNnEhIKBG5hbWUYAiABKAlSBG5hbWUihQEKD0hlbGxvU3ViUmVxdWVzdBIYCgdzdWJfbXNnGAEgASgJUgdzdWJfbXNnEhoKCHN1Yl9uYW1lGAIgASgJUghzdWJfbmFtZRI8Cgt1cGRhdGVfbWFzaxgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tSC3VwZGF0ZV9tYXNrIvEBChlIZWxsb1JlcXVlc3RXaXRoVmFsaWRhdG9yEhAKA21zZxgBIAEoCVIDbXNnEhIKBG5hbWUYAiABKAlSBG5hbWUSEAoDYWdlGAMgASgFUgNhZ2USLQoDc3ViGAQgASgLMhsuaGVsbG93b3JsZC5IZWxsb1N1YlJlcXVlc3RSA3N1YhIvCgRzdWJzGAUgAygLMhsuaGVsbG93b3JsZC5IZWxsb1N1YlJlcXVlc3RSBHN1YnMSPAoLdXBkYXRlX21hc2sYBiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrUgt1cGRhdGVfbWFzayI6CgpIZWxsb1JlcGx5EhgKB21lc3NhZ2UYASABKAlSB21lc3NhZ2USEgoEbmFtZRgCIAEoCVIEbmFtZSJBCg1SZXBlYXRlZFJlcGx5EjAKB3JlcGxpZXMYASADKAsyFi5oZWxsb3dvcmxkLkhlbGxvUmVwbHlSB3JlcGxpZXMy8gUKB0dyZWV0ZXISXQoIU2F5SGVsbG8SGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIfgtPkkwIZIhQvYXBpL3YxL2V4YW1wbGUvcG9zdDoBKhJfCgtTYXlIZWxsb0dldBIYLmhlbGxvd29ybGQuSGVsbG9SZXF1ZXN0GhYuaGVsbG93b3JsZC5IZWxsb1JlcGx5Ih6C0+STAhgSFi9hcGkvdjEvZXhhbXBsZS97bmFtZX0SeAoXU2F5SGVsbG9TZXJ2ZXJTaWRlRXZlbnQSGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIpgtPkkwIjEiEvYXBpL3YxL2V4YW1wbGUvc2VydmVyL3NzZS97bmFtZX0wARJ3ChRTYXlIZWxsb0NsaWVudFN0cmVhbRIYLmhlbGxvd29ybGQuSGVsbG9SZXF1ZXN0GhkuaGVsbG93b3JsZC5SZXBlYXRlZFJlcGx5IiiC0+STAiIiHS9hcGkvdjEvZXhhbXBsZS9jbGllbnQvc3RyZWFtOgEqKAEScwoRU2F5SGVsbG9XZWJzb2NrZXQSGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIogtPkkwIiEiAvYXBpL3YxL2V4YW1wbGUvc2VydmVyL3dlYnNvY2tldCgBMAESWAoMU2F5SGVsbG9Cb2R5EhQuZ29vZ2xlLmFwaS5IdHRwQm9keRoULmdvb2dsZS5hcGkuSHR0cEJvZHkiHILT5JMCFiIUL2FwaS92MS9leGFtcGxlL2JvZHkSZQoNU2F5SGVsbG9FcnJvchIYLmhlbGxvd29ybGQuRXJyb3JSZXF1ZXN0GhYuaGVsbG93b3JsZC5IZWxsb1JlcGx5IiKC0+STAhwSGi9hcGkvdjEvZXhhbXBsZS9lcnJvci90ZXN0QjkKGGlvLmdycGMuZXhhbXBsZXMuZXhhbXBsZUIMRXhhbXBsZVByb3RvUAFaB2V4YW1wbGWiAgNITFdiBnByb3RvMw==", + "name": "test", + "service_name": "test", + "description": "test", + "endpoints": [ + { + "addr": "127.0.0.1:21213", + "weight": 0 + }, + { + "addr": "127.0.0.1:21214", + "weight": 0 + }, + { + "addr": "127.0.0.1:21215", + "weight": 0 + } + ], + "balance": "RR", + "tags": [ + "test" + ] +} +``` +- descriptor_set: 通过对`protoc --descriptor_set_out`生成的文件描述符集合二进制文件进行base64编码,获取得到的字符串 +- name:Endpoint 名称 +- service_name:服务名称 +- description:服务描述 +- endpoints:gRPC服务的访问地址列表其中`weight`是gRPC服务器的优先级,该参数应用于使用优先级作为参数的负载均衡算法 +- balance:负载均衡算法,可选项包括 + - `RR(RoundRobin 轮询算法)` + - `WRR(WeightedRoundRobin 加权轮询算法)` + - `LC(LeastConnections 最小连接数算法)` + - `WLC(WeightedLeastConnections 加权最小连接数算法)` + - `CH(ConsistentHash 基于IP的一致性哈希算法)` + - `SED(ShortestExpectedDelay 最短期望延迟算法)` + - `NQ(NonQueueing 非排队算法)` + +- tags:服务标签 + +## 生成proto 文件描述符集合 +```bash +protoc --descriptor_set_out=example.pb --include_imports --proto_path=./ example.proto +``` +## 添加方式 + +### 命令行添加 + +begonia支持通过命令行添加服务,命令行参数如下: +```shell +begonia endpoint add -n "example" -d /data/work/begonia-org/begonia/example/example.pb -p 127.0.0.1:1949 -p 127.0.0.1:2024 -b RR -t test -t test2 +``` +- -n:Endpoint 名称 +- -d:服务描述符集合文件路径 +- -p:服务地址 +- -b:负载均衡算法 +- -t:服务标签 + +### HTTP API添加 +```bash +curl --location 'http://127.0.0.1:12138/api/v1/endpoints' \ +--header 'x-api-key: 1234567890' \ +--header 'Accept: application/json' \ +--header 'Content-Type: application/json' \ +--data '{ + "descriptor_set": "CqwFChVnb29nbGUvYXBpL2h0dHAucHJvdG8SCmdvb2dsZS5hcGkieQoESHR0cBIqCgVydWxlcxgBIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVSBXJ1bGVzEkUKH2Z1bGx5X2RlY29kZV9yZXNlcnZlZF9leHBhbnNpb24YAiABKAhSHGZ1bGx5RGVjb2RlUmVzZXJ2ZWRFeHBhbnNpb24i2gIKCEh0dHBSdWxlEhoKCHNlbGVjdG9yGAEgASgJUghzZWxlY3RvchISCgNnZXQYAiABKAlIAFIDZ2V0EhIKA3B1dBgDIAEoCUgAUgNwdXQSFAoEcG9zdBgEIAEoCUgAUgRwb3N0EhgKBmRlbGV0ZRgFIAEoCUgAUgZkZWxldGUSFgoFcGF0Y2gYBiABKAlIAFIFcGF0Y2gSNwoGY3VzdG9tGAggASgLMh0uZ29vZ2xlLmFwaS5DdXN0b21IdHRwUGF0dGVybkgAUgZjdXN0b20SEgoEYm9keRgHIAEoCVIEYm9keRIjCg1yZXNwb25zZV9ib2R5GAwgASgJUgxyZXNwb25zZUJvZHkSRQoTYWRkaXRpb25hbF9iaW5kaW5ncxgLIAMoCzIULmdvb2dsZS5hcGkuSHR0cFJ1bGVSEmFkZGl0aW9uYWxCaW5kaW5nc0IJCgdwYXR0ZXJuIjsKEUN1c3RvbUh0dHBQYXR0ZXJuEhIKBGtpbmQYASABKAlSBGtpbmQSEgoEcGF0aBgCIAEoCVIEcGF0aEJqCg5jb20uZ29vZ2xlLmFwaUIJSHR0cFByb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25z+AEBogIER0FQSWIGcHJvdG8zCuFCCiBnb29nbGUvcHJvdG9idWYvZGVzY3JpcHRvci5wcm90bxIPZ29vZ2xlLnByb3RvYnVmIk0KEUZpbGVEZXNjcmlwdG9yU2V0EjgKBGZpbGUYASADKAsyJC5nb29nbGUucHJvdG9idWYuRmlsZURlc2NyaXB0b3JQcm90b1IEZmlsZSL+BAoTRmlsZURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEhgKB3BhY2thZ2UYAiABKAlSB3BhY2thZ2USHgoKZGVwZW5kZW5jeRgDIAMoCVIKZGVwZW5kZW5jeRIrChFwdWJsaWNfZGVwZW5kZW5jeRgKIAMoBVIQcHVibGljRGVwZW5kZW5jeRInCg93ZWFrX2RlcGVuZGVuY3kYCyADKAVSDndlYWtEZXBlbmRlbmN5EkMKDG1lc3NhZ2VfdHlwZRgEIAMoCzIgLmdvb2dsZS5wcm90b2J1Zi5EZXNjcmlwdG9yUHJvdG9SC21lc3NhZ2VUeXBlEkEKCWVudW1fdHlwZRgFIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5FbnVtRGVzY3JpcHRvclByb3RvUghlbnVtVHlwZRJBCgdzZXJ2aWNlGAYgAygLMicuZ29vZ2xlLnByb3RvYnVmLlNlcnZpY2VEZXNjcmlwdG9yUHJvdG9SB3NlcnZpY2USQwoJZXh0ZW5zaW9uGAcgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvUglleHRlbnNpb24SNgoHb3B0aW9ucxgIIAEoCzIcLmdvb2dsZS5wcm90b2J1Zi5GaWxlT3B0aW9uc1IHb3B0aW9ucxJJChBzb3VyY2VfY29kZV9pbmZvGAkgASgLMh8uZ29vZ2xlLnByb3RvYnVmLlNvdXJjZUNvZGVJbmZvUg5zb3VyY2VDb2RlSW5mbxIWCgZzeW50YXgYDCABKAlSBnN5bnRheBIYCgdlZGl0aW9uGA0gASgJUgdlZGl0aW9uIrkGCg9EZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRI7CgVmaWVsZBgCIAMoCzIlLmdvb2dsZS5wcm90b2J1Zi5GaWVsZERlc2NyaXB0b3JQcm90b1IFZmllbGQSQwoJZXh0ZW5zaW9uGAYgAygLMiUuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvUglleHRlbnNpb24SQQoLbmVzdGVkX3R5cGUYAyADKAsyIC5nb29nbGUucHJvdG9idWYuRGVzY3JpcHRvclByb3RvUgpuZXN0ZWRUeXBlEkEKCWVudW1fdHlwZRgEIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5FbnVtRGVzY3JpcHRvclByb3RvUghlbnVtVHlwZRJYCg9leHRlbnNpb25fcmFuZ2UYBSADKAsyLy5nb29nbGUucHJvdG9idWYuRGVzY3JpcHRvclByb3RvLkV4dGVuc2lvblJhbmdlUg5leHRlbnNpb25SYW5nZRJECgpvbmVvZl9kZWNsGAggAygLMiUuZ29vZ2xlLnByb3RvYnVmLk9uZW9mRGVzY3JpcHRvclByb3RvUglvbmVvZkRlY2wSOQoHb3B0aW9ucxgHIAEoCzIfLmdvb2dsZS5wcm90b2J1Zi5NZXNzYWdlT3B0aW9uc1IHb3B0aW9ucxJVCg5yZXNlcnZlZF9yYW5nZRgJIAMoCzIuLmdvb2dsZS5wcm90b2J1Zi5EZXNjcmlwdG9yUHJvdG8uUmVzZXJ2ZWRSYW5nZVINcmVzZXJ2ZWRSYW5nZRIjCg1yZXNlcnZlZF9uYW1lGAogAygJUgxyZXNlcnZlZE5hbWUaegoORXh0ZW5zaW9uUmFuZ2USFAoFc3RhcnQYASABKAVSBXN0YXJ0EhAKA2VuZBgCIAEoBVIDZW5kEkAKB29wdGlvbnMYAyABKAsyJi5nb29nbGUucHJvdG9idWYuRXh0ZW5zaW9uUmFuZ2VPcHRpb25zUgdvcHRpb25zGjcKDVJlc2VydmVkUmFuZ2USFAoFc3RhcnQYASABKAVSBXN0YXJ0EhAKA2VuZBgCIAEoBVIDZW5kInwKFUV4dGVuc2lvblJhbmdlT3B0aW9ucxJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACIsEGChRGaWVsZERlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEhYKBm51bWJlchgDIAEoBVIGbnVtYmVyEkEKBWxhYmVsGAQgASgOMisuZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvLkxhYmVsUgVsYWJlbBI+CgR0eXBlGAUgASgOMiouZ29vZ2xlLnByb3RvYnVmLkZpZWxkRGVzY3JpcHRvclByb3RvLlR5cGVSBHR5cGUSGwoJdHlwZV9uYW1lGAYgASgJUgh0eXBlTmFtZRIaCghleHRlbmRlZRgCIAEoCVIIZXh0ZW5kZWUSIwoNZGVmYXVsdF92YWx1ZRgHIAEoCVIMZGVmYXVsdFZhbHVlEh8KC29uZW9mX2luZGV4GAkgASgFUgpvbmVvZkluZGV4EhsKCWpzb25fbmFtZRgKIAEoCVIIanNvbk5hbWUSNwoHb3B0aW9ucxgIIAEoCzIdLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnNSB29wdGlvbnMSJwoPcHJvdG8zX29wdGlvbmFsGBEgASgIUg5wcm90bzNPcHRpb25hbCK2AgoEVHlwZRIPCgtUWVBFX0RPVUJMRRABEg4KClRZUEVfRkxPQVQQAhIOCgpUWVBFX0lOVDY0EAMSDwoLVFlQRV9VSU5UNjQQBBIOCgpUWVBFX0lOVDMyEAUSEAoMVFlQRV9GSVhFRDY0EAYSEAoMVFlQRV9GSVhFRDMyEAcSDQoJVFlQRV9CT09MEAgSDwoLVFlQRV9TVFJJTkcQCRIOCgpUWVBFX0dST1VQEAoSEAoMVFlQRV9NRVNTQUdFEAsSDgoKVFlQRV9CWVRFUxAMEg8KC1RZUEVfVUlOVDMyEA0SDQoJVFlQRV9FTlVNEA4SEQoNVFlQRV9TRklYRUQzMhAPEhEKDVRZUEVfU0ZJWEVENjQQEBIPCgtUWVBFX1NJTlQzMhAREg8KC1RZUEVfU0lOVDY0EBIiQwoFTGFiZWwSEgoOTEFCRUxfT1BUSU9OQUwQARISCg5MQUJFTF9SRVFVSVJFRBACEhIKDkxBQkVMX1JFUEVBVEVEEAMiYwoUT25lb2ZEZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRI3CgdvcHRpb25zGAIgASgLMh0uZ29vZ2xlLnByb3RvYnVmLk9uZW9mT3B0aW9uc1IHb3B0aW9ucyLjAgoTRW51bURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEj8KBXZhbHVlGAIgAygLMikuZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1ZURlc2NyaXB0b3JQcm90b1IFdmFsdWUSNgoHb3B0aW9ucxgDIAEoCzIcLmdvb2dsZS5wcm90b2J1Zi5FbnVtT3B0aW9uc1IHb3B0aW9ucxJdCg5yZXNlcnZlZF9yYW5nZRgEIAMoCzI2Lmdvb2dsZS5wcm90b2J1Zi5FbnVtRGVzY3JpcHRvclByb3RvLkVudW1SZXNlcnZlZFJhbmdlUg1yZXNlcnZlZFJhbmdlEiMKDXJlc2VydmVkX25hbWUYBSADKAlSDHJlc2VydmVkTmFtZRo7ChFFbnVtUmVzZXJ2ZWRSYW5nZRIUCgVzdGFydBgBIAEoBVIFc3RhcnQSEAoDZW5kGAIgASgFUgNlbmQigwEKGEVudW1WYWx1ZURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEhYKBm51bWJlchgCIAEoBVIGbnVtYmVyEjsKB29wdGlvbnMYAyABKAsyIS5nb29nbGUucHJvdG9idWYuRW51bVZhbHVlT3B0aW9uc1IHb3B0aW9ucyKnAQoWU2VydmljZURlc2NyaXB0b3JQcm90bxISCgRuYW1lGAEgASgJUgRuYW1lEj4KBm1ldGhvZBgCIAMoCzImLmdvb2dsZS5wcm90b2J1Zi5NZXRob2REZXNjcmlwdG9yUHJvdG9SBm1ldGhvZBI5CgdvcHRpb25zGAMgASgLMh8uZ29vZ2xlLnByb3RvYnVmLlNlcnZpY2VPcHRpb25zUgdvcHRpb25zIokCChVNZXRob2REZXNjcmlwdG9yUHJvdG8SEgoEbmFtZRgBIAEoCVIEbmFtZRIdCgppbnB1dF90eXBlGAIgASgJUglpbnB1dFR5cGUSHwoLb3V0cHV0X3R5cGUYAyABKAlSCm91dHB1dFR5cGUSOAoHb3B0aW9ucxgEIAEoCzIeLmdvb2dsZS5wcm90b2J1Zi5NZXRob2RPcHRpb25zUgdvcHRpb25zEjAKEGNsaWVudF9zdHJlYW1pbmcYBSABKAg6BWZhbHNlUg9jbGllbnRTdHJlYW1pbmcSMAoQc2VydmVyX3N0cmVhbWluZxgGIAEoCDoFZmFsc2VSD3NlcnZlclN0cmVhbWluZyKRCQoLRmlsZU9wdGlvbnMSIQoMamF2YV9wYWNrYWdlGAEgASgJUgtqYXZhUGFja2FnZRIwChRqYXZhX291dGVyX2NsYXNzbmFtZRgIIAEoCVISamF2YU91dGVyQ2xhc3NuYW1lEjUKE2phdmFfbXVsdGlwbGVfZmlsZXMYCiABKAg6BWZhbHNlUhFqYXZhTXVsdGlwbGVGaWxlcxJECh1qYXZhX2dlbmVyYXRlX2VxdWFsc19hbmRfaGFzaBgUIAEoCEICGAFSGWphdmFHZW5lcmF0ZUVxdWFsc0FuZEhhc2gSOgoWamF2YV9zdHJpbmdfY2hlY2tfdXRmOBgbIAEoCDoFZmFsc2VSE2phdmFTdHJpbmdDaGVja1V0ZjgSUwoMb3B0aW1pemVfZm9yGAkgASgOMikuZ29vZ2xlLnByb3RvYnVmLkZpbGVPcHRpb25zLk9wdGltaXplTW9kZToFU1BFRURSC29wdGltaXplRm9yEh0KCmdvX3BhY2thZ2UYCyABKAlSCWdvUGFja2FnZRI1ChNjY19nZW5lcmljX3NlcnZpY2VzGBAgASgIOgVmYWxzZVIRY2NHZW5lcmljU2VydmljZXMSOQoVamF2YV9nZW5lcmljX3NlcnZpY2VzGBEgASgIOgVmYWxzZVITamF2YUdlbmVyaWNTZXJ2aWNlcxI1ChNweV9nZW5lcmljX3NlcnZpY2VzGBIgASgIOgVmYWxzZVIRcHlHZW5lcmljU2VydmljZXMSNwoUcGhwX2dlbmVyaWNfc2VydmljZXMYKiABKAg6BWZhbHNlUhJwaHBHZW5lcmljU2VydmljZXMSJQoKZGVwcmVjYXRlZBgXIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSLgoQY2NfZW5hYmxlX2FyZW5hcxgfIAEoCDoEdHJ1ZVIOY2NFbmFibGVBcmVuYXMSKgoRb2JqY19jbGFzc19wcmVmaXgYJCABKAlSD29iamNDbGFzc1ByZWZpeBIpChBjc2hhcnBfbmFtZXNwYWNlGCUgASgJUg9jc2hhcnBOYW1lc3BhY2USIQoMc3dpZnRfcHJlZml4GCcgASgJUgtzd2lmdFByZWZpeBIoChBwaHBfY2xhc3NfcHJlZml4GCggASgJUg5waHBDbGFzc1ByZWZpeBIjCg1waHBfbmFtZXNwYWNlGCkgASgJUgxwaHBOYW1lc3BhY2USNAoWcGhwX21ldGFkYXRhX25hbWVzcGFjZRgsIAEoCVIUcGhwTWV0YWRhdGFOYW1lc3BhY2USIQoMcnVieV9wYWNrYWdlGC0gASgJUgtydWJ5UGFja2FnZRJYChR1bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlvblITdW5pbnRlcnByZXRlZE9wdGlvbiI6CgxPcHRpbWl6ZU1vZGUSCQoFU1BFRUQQARINCglDT0RFX1NJWkUQAhIQCgxMSVRFX1JVTlRJTUUQAyoJCOgHEICAgIACSgQIJhAnIrsDCg5NZXNzYWdlT3B0aW9ucxI8ChdtZXNzYWdlX3NldF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2VSFG1lc3NhZ2VTZXRXaXJlRm9ybWF0EkwKH25vX3N0YW5kYXJkX2Rlc2NyaXB0b3JfYWNjZXNzb3IYAiABKAg6BWZhbHNlUhxub1N0YW5kYXJkRGVzY3JpcHRvckFjY2Vzc29yEiUKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNlUgpkZXByZWNhdGVkEhsKCW1hcF9lbnRyeRgHIAEoCFIIbWFwRW50cnkSVgomZGVwcmVjYXRlZF9sZWdhY3lfanNvbl9maWVsZF9jb25mbGljdHMYCyABKAhCAhgBUiJkZXByZWNhdGVkTGVnYWN5SnNvbkZpZWxkQ29uZmxpY3RzElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgEEAVKBAgFEAZKBAgGEAdKBAgIEAlKBAgJEAoitwgKDEZpZWxkT3B0aW9ucxJBCgVjdHlwZRgBIAEoDjIjLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuQ1R5cGU6BlNUUklOR1IFY3R5cGUSFgoGcGFja2VkGAIgASgIUgZwYWNrZWQSRwoGanN0eXBlGAYgASgOMiQuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5KU1R5cGU6CUpTX05PUk1BTFIGanN0eXBlEhkKBGxhenkYBSABKAg6BWZhbHNlUgRsYXp5Ei4KD3VudmVyaWZpZWRfbGF6eRgPIAEoCDoFZmFsc2VSDnVudmVyaWZpZWRMYXp5EiUKCmRlcHJlY2F0ZWQYAyABKAg6BWZhbHNlUgpkZXByZWNhdGVkEhkKBHdlYWsYCiABKAg6BWZhbHNlUgR3ZWFrEigKDGRlYnVnX3JlZGFjdBgQIAEoCDoFZmFsc2VSC2RlYnVnUmVkYWN0EksKCXJldGVudGlvbhgRIAEoDjItLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE9wdGlvbnMuT3B0aW9uUmV0ZW50aW9uUglyZXRlbnRpb24SRgoGdGFyZ2V0GBIgASgOMi4uZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5PcHRpb25UYXJnZXRUeXBlUgZ0YXJnZXQSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24iLwoFQ1R5cGUSCgoGU1RSSU5HEAASCAoEQ09SRBABEhAKDFNUUklOR19QSUVDRRACIjUKBkpTVHlwZRINCglKU19OT1JNQUwQABINCglKU19TVFJJTkcQARINCglKU19OVU1CRVIQAiJVCg9PcHRpb25SZXRlbnRpb24SFQoRUkVURU5USU9OX1VOS05PV04QABIVChFSRVRFTlRJT05fUlVOVElNRRABEhQKEFJFVEVOVElPTl9TT1VSQ0UQAiKMAgoQT3B0aW9uVGFyZ2V0VHlwZRIXChNUQVJHRVRfVFlQRV9VTktOT1dOEAASFAoQVEFSR0VUX1RZUEVfRklMRRABEh8KG1RBUkdFVF9UWVBFX0VYVEVOU0lPTl9SQU5HRRACEhcKE1RBUkdFVF9UWVBFX01FU1NBR0UQAxIVChFUQVJHRVRfVFlQRV9GSUVMRBAEEhUKEVRBUkdFVF9UWVBFX09ORU9GEAUSFAoQVEFSR0VUX1RZUEVfRU5VTRAGEhoKFlRBUkdFVF9UWVBFX0VOVU1fRU5UUlkQBxIXChNUQVJHRVRfVFlQRV9TRVJWSUNFEAgSFgoSVEFSR0VUX1RZUEVfTUVUSE9EEAkqCQjoBxCAgICAAkoECAQQBSJzCgxPbmVvZk9wdGlvbnMSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKYAgoLRW51bU9wdGlvbnMSHwoLYWxsb3dfYWxpYXMYAiABKAhSCmFsbG93QWxpYXMSJQoKZGVwcmVjYXRlZBgDIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSVgomZGVwcmVjYXRlZF9sZWdhY3lfanNvbl9maWVsZF9jb25mbGljdHMYBiABKAhCAhgBUiJkZXByZWNhdGVkTGVnYWN5SnNvbkZpZWxkQ29uZmxpY3RzElgKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uUhN1bmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAJKBAgFEAYingEKEEVudW1WYWx1ZU9wdGlvbnMSJQoKZGVwcmVjYXRlZBgBIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKcAQoOU2VydmljZU9wdGlvbnMSJQoKZGVwcmVjYXRlZBghIAEoCDoFZmFsc2VSCmRlcHJlY2F0ZWQSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiLgAgoNTWV0aG9kT3B0aW9ucxIlCgpkZXByZWNhdGVkGCEgASgIOgVmYWxzZVIKZGVwcmVjYXRlZBJxChFpZGVtcG90ZW5jeV9sZXZlbBgiIAEoDjIvLmdvb2dsZS5wcm90b2J1Zi5NZXRob2RPcHRpb25zLklkZW1wb3RlbmN5TGV2ZWw6E0lERU1QT1RFTkNZX1VOS05PV05SEGlkZW1wb3RlbmN5TGV2ZWwSWAoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb25SE3VuaW50ZXJwcmV0ZWRPcHRpb24iUAoQSWRlbXBvdGVuY3lMZXZlbBIXChNJREVNUE9URU5DWV9VTktOT1dOEAASEwoPTk9fU0lERV9FRkZFQ1RTEAESDgoKSURFTVBPVEVOVBACKgkI6AcQgICAgAIimgMKE1VuaW50ZXJwcmV0ZWRPcHRpb24SQQoEbmFtZRgCIAMoCzItLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uLk5hbWVQYXJ0UgRuYW1lEikKEGlkZW50aWZpZXJfdmFsdWUYAyABKAlSD2lkZW50aWZpZXJWYWx1ZRIsChJwb3NpdGl2ZV9pbnRfdmFsdWUYBCABKARSEHBvc2l0aXZlSW50VmFsdWUSLAoSbmVnYXRpdmVfaW50X3ZhbHVlGAUgASgDUhBuZWdhdGl2ZUludFZhbHVlEiEKDGRvdWJsZV92YWx1ZRgGIAEoAVILZG91YmxlVmFsdWUSIQoMc3RyaW5nX3ZhbHVlGAcgASgMUgtzdHJpbmdWYWx1ZRInCg9hZ2dyZWdhdGVfdmFsdWUYCCABKAlSDmFnZ3JlZ2F0ZVZhbHVlGkoKCE5hbWVQYXJ0EhsKCW5hbWVfcGFydBgBIAIoCVIIbmFtZVBhcnQSIQoMaXNfZXh0ZW5zaW9uGAIgAigIUgtpc0V4dGVuc2lvbiKnAgoOU291cmNlQ29kZUluZm8SRAoIbG9jYXRpb24YASADKAsyKC5nb29nbGUucHJvdG9idWYuU291cmNlQ29kZUluZm8uTG9jYXRpb25SCGxvY2F0aW9uGs4BCghMb2NhdGlvbhIWCgRwYXRoGAEgAygFQgIQAVIEcGF0aBIWCgRzcGFuGAIgAygFQgIQAVIEc3BhbhIpChBsZWFkaW5nX2NvbW1lbnRzGAMgASgJUg9sZWFkaW5nQ29tbWVudHMSKwoRdHJhaWxpbmdfY29tbWVudHMYBCABKAlSEHRyYWlsaW5nQ29tbWVudHMSOgoZbGVhZGluZ19kZXRhY2hlZF9jb21tZW50cxgGIAMoCVIXbGVhZGluZ0RldGFjaGVkQ29tbWVudHMi0AIKEUdlbmVyYXRlZENvZGVJbmZvEk0KCmFubm90YXRpb24YASADKAsyLS5nb29nbGUucHJvdG9idWYuR2VuZXJhdGVkQ29kZUluZm8uQW5ub3RhdGlvblIKYW5ub3RhdGlvbhrrAQoKQW5ub3RhdGlvbhIWCgRwYXRoGAEgAygFQgIQAVIEcGF0aBIfCgtzb3VyY2VfZmlsZRgCIAEoCVIKc291cmNlRmlsZRIUCgViZWdpbhgDIAEoBVIFYmVnaW4SEAoDZW5kGAQgASgFUgNlbmQSUgoIc2VtYW50aWMYBSABKA4yNi5nb29nbGUucHJvdG9idWYuR2VuZXJhdGVkQ29kZUluZm8uQW5ub3RhdGlvbi5TZW1hbnRpY1IIc2VtYW50aWMiKAoIU2VtYW50aWMSCAoETk9ORRAAEgcKA1NFVBABEgkKBUFMSUFTEAJCfgoTY29tLmdvb2dsZS5wcm90b2J1ZkIQRGVzY3JpcHRvclByb3Rvc0gBWi1nb29nbGUuZ29sYW5nLm9yZy9wcm90b2J1Zi90eXBlcy9kZXNjcmlwdG9ycGL4AQGiAgNHUEKqAhpHb29nbGUuUHJvdG9idWYuUmVmbGVjdGlvbgqoAgocZ29vZ2xlL2FwaS9hbm5vdGF0aW9ucy5wcm90bxIKZ29vZ2xlLmFwaRoVZ29vZ2xlL2FwaS9odHRwLnByb3RvGiBnb29nbGUvcHJvdG9idWYvZGVzY3JpcHRvci5wcm90bzpLCgRodHRwEh4uZ29vZ2xlLnByb3RvYnVmLk1ldGhvZE9wdGlvbnMYsMq8IiABKAsyFC5nb29nbGUuYXBpLkh0dHBSdWxlUgRodHRwQm4KDmNvbS5nb29nbGUuYXBpQhBBbm5vdGF0aW9uc1Byb3RvUAFaQWdvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2Fubm90YXRpb25zO2Fubm90YXRpb25zogIER0FQSWIGcHJvdG8zCuYBCiBnb29nbGUvcHJvdG9idWYvZmllbGRfbWFzay5wcm90bxIPZ29vZ2xlLnByb3RvYnVmIiEKCUZpZWxkTWFzaxIUCgVwYXRocxgBIAMoCVIFcGF0aHNChQEKE2NvbS5nb29nbGUucHJvdG9idWZCDkZpZWxkTWFza1Byb3RvUAFaMmdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3R5cGVzL2tub3duL2ZpZWxkbWFza3Bi+AEBogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90bzMK5AEKGWdvb2dsZS9wcm90b2J1Zi9hbnkucHJvdG8SD2dvb2dsZS5wcm90b2J1ZiI2CgNBbnkSGQoIdHlwZV91cmwYASABKAlSB3R5cGVVcmwSFAoFdmFsdWUYAiABKAxSBXZhbHVlQnYKE2NvbS5nb29nbGUucHJvdG9idWZCCEFueVByb3RvUAFaLGdvb2dsZS5nb2xhbmcub3JnL3Byb3RvYnVmL3R5cGVzL2tub3duL2FueXBiogIDR1BCqgIeR29vZ2xlLlByb3RvYnVmLldlbGxLbm93blR5cGVzYgZwcm90bzMKrQIKGWdvb2dsZS9hcGkvaHR0cGJvZHkucHJvdG8SCmdvb2dsZS5hcGkaGWdvb2dsZS9wcm90b2J1Zi9hbnkucHJvdG8idwoISHR0cEJvZHkSIQoMY29udGVudF90eXBlGAEgASgJUgtjb250ZW50VHlwZRISCgRkYXRhGAIgASgMUgRkYXRhEjQKCmV4dGVuc2lvbnMYAyADKAsyFC5nb29nbGUucHJvdG9idWYuQW55UgpleHRlbnNpb25zQmgKDmNvbS5nb29nbGUuYXBpQg1IdHRwQm9keVByb3RvUAFaO2dvb2dsZS5nb2xhbmcub3JnL2dlbnByb3RvL2dvb2dsZWFwaXMvYXBpL2h0dHBib2R5O2h0dHBib2R5+AEBogIER0FQSWIGcHJvdG8zCrcMCg1leGFtcGxlLnByb3RvEgpoZWxsb3dvcmxkGhxnb29nbGUvYXBpL2Fubm90YXRpb25zLnByb3RvGiBnb29nbGUvcHJvdG9idWYvZmllbGRfbWFzay5wcm90bxogZ29vZ2xlL3Byb3RvYnVmL2Rlc2NyaXB0b3IucHJvdG8aGWdvb2dsZS9hcGkvaHR0cGJvZHkucHJvdG8iNAoMRXJyb3JSZXF1ZXN0EhAKA21zZxgBIAEoCVIDbXNnEhIKBGNvZGUYAiABKAVSBGNvZGUiNAoMSGVsbG9SZXF1ZXN0EhAKA21zZxgBIAEoCVIDbXNnEhIKBG5hbWUYAiABKAlSBG5hbWUihQEKD0hlbGxvU3ViUmVxdWVzdBIYCgdzdWJfbXNnGAEgASgJUgdzdWJfbXNnEhoKCHN1Yl9uYW1lGAIgASgJUghzdWJfbmFtZRI8Cgt1cGRhdGVfbWFzaxgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE1hc2tSC3VwZGF0ZV9tYXNrIvEBChlIZWxsb1JlcXVlc3RXaXRoVmFsaWRhdG9yEhAKA21zZxgBIAEoCVIDbXNnEhIKBG5hbWUYAiABKAlSBG5hbWUSEAoDYWdlGAMgASgFUgNhZ2USLQoDc3ViGAQgASgLMhsuaGVsbG93b3JsZC5IZWxsb1N1YlJlcXVlc3RSA3N1YhIvCgRzdWJzGAUgAygLMhsuaGVsbG93b3JsZC5IZWxsb1N1YlJlcXVlc3RSBHN1YnMSPAoLdXBkYXRlX21hc2sYBiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrUgt1cGRhdGVfbWFzayI6CgpIZWxsb1JlcGx5EhgKB21lc3NhZ2UYASABKAlSB21lc3NhZ2USEgoEbmFtZRgCIAEoCVIEbmFtZSJBCg1SZXBlYXRlZFJlcGx5EjAKB3JlcGxpZXMYASADKAsyFi5oZWxsb3dvcmxkLkhlbGxvUmVwbHlSB3JlcGxpZXMy8gUKB0dyZWV0ZXISXQoIU2F5SGVsbG8SGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIfgtPkkwIZIhQvYXBpL3YxL2V4YW1wbGUvcG9zdDoBKhJfCgtTYXlIZWxsb0dldBIYLmhlbGxvd29ybGQuSGVsbG9SZXF1ZXN0GhYuaGVsbG93b3JsZC5IZWxsb1JlcGx5Ih6C0+STAhgSFi9hcGkvdjEvZXhhbXBsZS97bmFtZX0SeAoXU2F5SGVsbG9TZXJ2ZXJTaWRlRXZlbnQSGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIpgtPkkwIjEiEvYXBpL3YxL2V4YW1wbGUvc2VydmVyL3NzZS97bmFtZX0wARJ3ChRTYXlIZWxsb0NsaWVudFN0cmVhbRIYLmhlbGxvd29ybGQuSGVsbG9SZXF1ZXN0GhkuaGVsbG93b3JsZC5SZXBlYXRlZFJlcGx5IiiC0+STAiIiHS9hcGkvdjEvZXhhbXBsZS9jbGllbnQvc3RyZWFtOgEqKAEScwoRU2F5SGVsbG9XZWJzb2NrZXQSGC5oZWxsb3dvcmxkLkhlbGxvUmVxdWVzdBoWLmhlbGxvd29ybGQuSGVsbG9SZXBseSIogtPkkwIiEiAvYXBpL3YxL2V4YW1wbGUvc2VydmVyL3dlYnNvY2tldCgBMAESWAoMU2F5SGVsbG9Cb2R5EhQuZ29vZ2xlLmFwaS5IdHRwQm9keRoULmdvb2dsZS5hcGkuSHR0cEJvZHkiHILT5JMCFiIUL2FwaS92MS9leGFtcGxlL2JvZHkSZQoNU2F5SGVsbG9FcnJvchIYLmhlbGxvd29ybGQuRXJyb3JSZXF1ZXN0GhYuaGVsbG93b3JsZC5IZWxsb1JlcGx5IiKC0+STAhwSGi9hcGkvdjEvZXhhbXBsZS9lcnJvci90ZXN0QjkKGGlvLmdycGMuZXhhbXBsZXMuZXhhbXBsZUIMRXhhbXBsZVByb3RvUAFaB2V4YW1wbGWiAgNITFdiBnByb3RvMw==", + "name": "test", + "service_name": "test", + "description": "test", + "endpoints": [ + { + "addr": "127.0.0.1:21213", + "weight": 0 + }, + { + "addr": "127.0.0.1:21214", + "weight": 0 + }, + { + "addr": "127.0.0.1:21215", + "weight": 0 + } + ], + "balance": "RR", + "tags": [ + "test" + ] +}' +``` + +### SDK添加 +以go sdk为例, +- 安装go-sdk + +```bash +go get github.com/begonia-org/go-sdk +``` + +- 使用go-sdk + +```go +package main + +import ( + "context" + "os" + "log" + + goloadbalancer "github.com/begonia-org/go-loadbalancer" + api "github.com/begonia-org/go-sdk/api/endpoint/v1" + "github.com/begonia-org/go-sdk/client" +) + +func main() { + // Set access key and secret key to api client + apiClient := client.NewEndpointAPI("http://127.0.0.1:12138", "P6DKyCxyz2ewz4brsFPJtusyMLYh4uex", "5Ygsu0UiZJPil4eWf5XcXec3gYKcWiCfJtyGGQbHekUlEMf7KXM7pSWdev8UfhiI") + pb, _ := os.ReadFile("/data/work/begonia-org/begonia/example/example.pb") + endpoint := &api.EndpointSrvConfig{ + DescriptorSet: pb, + Name: "test", + ServiceName: "test", + Description: "test", + Balance: string(goloadbalancer.RRBalanceType), + Endpoints: []*api.EndpointMeta{ + { + Addr: "127.0.0.1:21213", + Weight: 0, + }, + { + Addr: "127.0.0.1:21214", + Weight: 0, + }, + { + Addr: "127.0.0.1:21215", + Weight: 0, + }, + }, + Tags: []string{"test"}, + } + _, err := apiClient.PostEndpointConfig(context.Background(), endpoint) + if err != nil { + panic(err) + } + log.Printf("Post endpoint success and endpoint id is %s", rsp.Id) + +} +``` +其中的AccessKey和SecretKey可以在初始化Begonia之后在~/.begonia/admin-app.json中查看 + +# 更新Endpoint \ No newline at end of file diff --git a/docs/overview.md b/docs/tutorials_zh.md similarity index 100% rename from docs/overview.md rename to docs/tutorials_zh.md diff --git a/internal/biz/data_test.go b/internal/biz/data_test.go index 5e8644a..b776cee 100644 --- a/internal/biz/data_test.go +++ b/internal/biz/data_test.go @@ -20,6 +20,7 @@ import ( appApi "github.com/begonia-org/go-sdk/api/app/v1" ep "github.com/begonia-org/go-sdk/api/endpoint/v1" api "github.com/begonia-org/go-sdk/api/user/v1" + user "github.com/begonia-org/go-sdk/api/user/v1" gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" c "github.com/smartystreets/goconvey/convey" clientv3 "go.etcd.io/etcd/client/v3" @@ -33,9 +34,10 @@ import ( // func TestCreateInBatches(t *testing.T) { func TestMain(m *testing.M) { log.Printf("Start testing") - setup() + // setup() code := m.Run() log.Printf("All tests passed with code %d", code) + // setup() } func setup() { @@ -78,6 +80,13 @@ func setup() { if err != nil { log.Fatalf("Failed to delete keys with prefix %s: %v", prefix, err) } + mysql := tiga.NewMySQLDao(conf) + mysql.RegisterTimeSerializer() + err = mysql.GetModel(&user.Users{}).Where("`group` = ?", "test-user-01").Delete(&user.Users{}).Error + if err != nil { + log.Fatalf("Failed to delete keys with prefix %s: %v", prefix, err) + } + log.Printf("Cleaned up test data") } func newDataOperatorUsecase() *biz.DataOperatorUsecase { diff --git a/internal/biz/endpoint/endpoint.go b/internal/biz/endpoint/endpoint.go index 7c3d348..085aff0 100644 --- a/internal/biz/endpoint/endpoint.go +++ b/internal/biz/endpoint/endpoint.go @@ -27,6 +27,7 @@ type EndpointRepo interface { List(ctx context.Context, keys []string) ([]*api.Endpoints, error) Put(ctx context.Context, endpoint *api.Endpoints) error Patch(ctx context.Context, id string, patch map[string]interface{}) error + ServiceNameExists(ctx context.Context, serviceName, exceptId string) error PutTags(ctx context.Context, id string, tags []string) error GetKeysByTags(ctx context.Context, tags []string) ([]string, error) } @@ -133,6 +134,10 @@ func (u *EndpointUsecase) Delete(ctx context.Context, uniqueKey string) error { func (u *EndpointUsecase) Get(ctx context.Context, uniqueKey string) (*api.Endpoints, error) { detailsKey := u.config.GetServiceKey(uniqueKey) + if !tiga.IsSnowflakeID(uniqueKey) { + detailsKey = u.config.GetServiceNameKey(uniqueKey) + } + // detailsKey := u.config.GetServiceKey(uniqueKey) value, err := u.repo.Get(ctx, detailsKey) if err != nil { return nil, gosdk.NewError(fmt.Errorf("%s:%w", pkg.ErrEndpointNotExists.Error(), err), int32(common.Code_NOT_FOUND), codes.NotFound, "get_endpoint") diff --git a/internal/biz/endpoint/endpoint_test.go b/internal/biz/endpoint/endpoint_test.go index 505b715..9939b49 100644 --- a/internal/biz/endpoint/endpoint_test.go +++ b/internal/biz/endpoint/endpoint_test.go @@ -35,6 +35,7 @@ import ( ) var epId = "" +var serviceName = "" func newEndpointBiz() *endpoint.EndpointUsecase { env := "dev" @@ -58,7 +59,7 @@ func testAddEndpoint(t *testing.T) { endpointSvr := &api.EndpointSrvConfig{ DescriptorSet: pb, Name: "test", - ServiceName: "test", + ServiceName: fmt.Sprintf("test-biz-endpoint-%s", time.Now().Format("20060102150405")), Description: "test", Balance: string(goloadbalancer.RRBalanceType), Endpoints: []*api.EndpointMeta{ @@ -81,12 +82,13 @@ func testAddEndpoint(t *testing.T) { c.So(err, c.ShouldBeNil) c.So(endpointId, c.ShouldNotBeEmpty) epId = endpointId + serviceName = endpointSvr.ServiceName }) c.Convey("Test Add Endpoint Fail", t, func() { endpointSvr := &api.EndpointSrvConfig{ DescriptorSet: pb, Name: "test", - ServiceName: "test", + ServiceName: fmt.Sprintf("test-biz-add-fail-endpoint-%s", time.Now().Format("20060102150405")), Description: "test", Balance: "test", Endpoints: []*api.EndpointMeta{ @@ -154,6 +156,9 @@ func testGetEndpoint(t *testing.T) { data, err := endpointBiz.Get(context.TODO(), epId) c.So(err, c.ShouldBeNil) c.So(data, c.ShouldNotBeEmpty) + data, err = endpointBiz.Get(context.TODO(), serviceName) + c.So(err, c.ShouldBeNil) + c.So(data, c.ShouldNotBeEmpty) }) } @@ -162,7 +167,7 @@ func testPatchEndpoint(t *testing.T) { c.Convey("Test Patch Endpoint", t, func() { updated_at, err := endpointBiz.Patch(context.TODO(), &api.EndpointSrvUpdateRequest{ UniqueKey: epId, - Name: "test_patch", + Name: fmt.Sprintf("test-test_patch-%s", time.Now().Format("20060102150405")), Description: "test patch", Tags: []string{"test-biz-1"}, UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"description", "tags"}}, @@ -206,7 +211,7 @@ func testPatchEndpoint(t *testing.T) { defer patch2.Reset() _, err = endpointBiz.Patch(context.TODO(), &api.EndpointSrvUpdateRequest{ UniqueKey: epId, - Name: "test_patch2", + Name: fmt.Sprintf("test-biz-test_patch2-%s", time.Now().Format("20060102150405")), UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, }) c.So(err, c.ShouldNotBeNil) @@ -218,7 +223,7 @@ func testPatchEndpoint(t *testing.T) { defer patch3.Reset() _, err = endpointBiz.Patch(context.TODO(), &api.EndpointSrvUpdateRequest{ UniqueKey: epId, - Name: "test_patch3", + Name: fmt.Sprintf("test-biz-test_patch3-%s", time.Now().Format("20060102150405")), UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, }) c.So(err, c.ShouldNotBeNil) @@ -229,7 +234,7 @@ func testPatchEndpoint(t *testing.T) { defer patch4.Reset() _, err = endpointBiz.Patch(context.TODO(), &api.EndpointSrvUpdateRequest{ UniqueKey: epId, - Name: "test_patch4", + Name: fmt.Sprintf("test-biz-test_patch4-%s", time.Now().Format("20060102150405")), UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, }) c.So(err, c.ShouldNotBeNil) @@ -240,12 +245,13 @@ func testPatchEndpoint(t *testing.T) { defer patch5.Reset() _, err = endpointBiz.Patch(context.TODO(), &api.EndpointSrvUpdateRequest{ UniqueKey: epId, - Name: "test_patch4", + Name: fmt.Sprintf("test-biz-test_patch4-%s", time.Now().Format("20060102150405")), UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"name"}}, }) patch5.Reset() c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "test watcher error") + }) } @@ -274,7 +280,7 @@ func testListEndpoints(t *testing.T) { id, err := endpointBiz.AddConfig(context.TODO(), &api.EndpointSrvConfig{ DescriptorSet: pb, Name: fmt.Sprintf("test-%d", i), - ServiceName: fmt.Sprintf("test-%d", i), + ServiceName: fmt.Sprintf("test-list-add-%s-%d", time.Now().Format("20060102150405"), i), Description: fmt.Sprintf("test-%d", i), Balance: string(goloadbalancer.RRBalanceType), Endpoints: []*api.EndpointMeta{ @@ -479,7 +485,6 @@ func TestEndpoint(t *testing.T) { t.Run("Test Get Endpoint", testGetEndpoint) t.Run("Test Patch Endpoint", testPatchEndpoint) t.Run("Test List Endpoint", testListEndpoints) - t.Run("Test Watcher Update", testWatcherUpdate) t.Run("Test Watcher Del", testWatcherDel) t.Run("Test Del Endpoint", testDelEndpoint) } diff --git a/internal/data/app_test.go b/internal/data/app_test.go index cebc8ed..0d50d14 100644 --- a/internal/data/app_test.go +++ b/internal/data/app_test.go @@ -252,7 +252,7 @@ func testListApp(t *testing.T) { env = begonia.Env } repo := NewAppRepo(cfg.ReadConfig(env), gateway.Log) - snk, _ := tiga.NewSnowflake(1) + snk, _ := tiga.NewSnowflake(3) rand := rand.New(rand.NewSource(time.Now().UnixNano())) status := []api.APPStatus{api.APPStatus_APP_ENABLED, api.APPStatus_APP_DISABLED} tags := [3]string{"tags-1", "tags-3", "tags-2"} diff --git a/internal/data/data_test.go b/internal/data/data_test.go index 3de085d..eacd7c1 100644 --- a/internal/data/data_test.go +++ b/internal/data/data_test.go @@ -9,15 +9,17 @@ import ( "github.com/begonia-org/begonia" cfg "github.com/begonia-org/begonia/config" "github.com/spark-lence/tiga" + user "github.com/begonia-org/go-sdk/api/user/v1" clientv3 "go.etcd.io/etcd/client/v3" ) // func TestCreateInBatches(t *testing.T) { func TestMain(m *testing.M) { log.Printf("Start testing") - setup() + // setup() code := m.Run() log.Printf("All tests passed with code %d", code) + // setup() } func setup() { @@ -61,4 +63,11 @@ func setup() { if err != nil { log.Fatalf("Failed to delete keys with prefix %s: %v", prefix, err) } + mysql := tiga.NewMySQLDao(conf) + mysql.RegisterTimeSerializer() + err=mysql.GetModel(&user.Users{}).Where("`group` = ?", "test-user-01").Delete(&user.Users{}).Error + if err != nil { + log.Fatalf("Failed to delete keys with prefix %s: %v", prefix, err) + } + log.Printf("Cleaned up test data") } diff --git a/internal/data/endpoint.go b/internal/data/endpoint.go index 7de6d5a..066935b 100644 --- a/internal/data/endpoint.go +++ b/internal/data/endpoint.go @@ -47,16 +47,40 @@ func (e *endpointRepoImpl) Del(ctx context.Context, id string) error { } return nil } +func (e *endpointRepoImpl) ServiceNameExists(ctx context.Context, name, id string) error { + srvNameKey := e.cfg.GetServiceNameKey(name) + if val, err := e.Get(ctx, srvNameKey); err != nil || val != "" { + if val != "" { + ep := make(map[string]interface{}) + err := json.Unmarshal([]byte(val), &ep) + if err != nil { + return fmt.Errorf("unmarshal service name error: %w", err) + } + if ep["uid"] != id { + return fmt.Errorf("%s service name already exists in %s",ep["uid"],id) + } + return nil + } + return fmt.Errorf("get service name fail: %w", err) + } + return nil +} func (e *endpointRepoImpl) Put(ctx context.Context, endpoint *api.Endpoints) error { + ops := make([]clientv3.Op, 0) srvKey := e.cfg.GetServiceKey(endpoint.Key) for _, tag := range endpoint.Tags { tagKey := e.cfg.GetTagsKey(tag, endpoint.Key) ops = append(ops, clientv3.OpPut(tagKey, srvKey)) } - + if err := e.ServiceNameExists(ctx, endpoint.ServiceName, endpoint.Key); err != nil { + return err + } + srvNameKey := e.cfg.GetServiceNameKey(endpoint.ServiceName) details, _ := json.Marshal(endpoint) ops = append(ops, clientv3.OpPut(srvKey, string(details))) + ops = append(ops, clientv3.OpPut(srvNameKey, string(details))) + ok, err := e.data.PutEtcdWithTxn(ctx, ops) if err != nil || !ok { // log.Printf("put endpoint fail: %s", err.Error()) @@ -151,6 +175,16 @@ func (e *endpointRepoImpl) Patch(ctx context.Context, id string, patch map[strin if err != nil { return err } + // check service name exists + if serviceName, ok := patch["service_name"]; ok { + if val, ok := serviceName.(string); ok { + if err := e.ServiceNameExists(ctx, val, id); err != nil { + return err + } + } + + } + ops := make([]clientv3.Op, 0) // 更新tags if tags, ok := patch["tags"]; ok && tags != nil { @@ -213,12 +247,9 @@ func (e *endpointRepoImpl) PutTags(ctx context.Context, id string, tags []string ops = append(ops, clientv3.OpPut(srvKey, string(updated))) ok, err := e.data.PutEtcdWithTxn(ctx, ops) - if err != nil { + if err != nil||!ok { return fmt.Errorf("put tags fail: %w", err) } - if !ok { - return fmt.Errorf("put tags fail") - } return nil } diff --git a/internal/data/endpoint_test.go b/internal/data/endpoint_test.go index cfff2c1..490891d 100644 --- a/internal/data/endpoint_test.go +++ b/internal/data/endpoint_test.go @@ -23,6 +23,8 @@ import ( ) var endpointId = "" +var endpointId2 = "" +var serviceName = "" var tag = fmt.Sprintf("test-endpoint-data-%s", time.Now().Format("20060102150405")) var tag3 = fmt.Sprintf("test3-%s", time.Now().Format("20060102150405")) @@ -40,11 +42,11 @@ func putTest(t *testing.T) { repo := NewEndpointRepo(conf, gateway.Log) snk, _ := tiga.NewSnowflake(1) endpointId = snk.GenerateIDString() - err := repo.Put(context.Background(), &api.Endpoints{ + ep := &api.Endpoints{ Key: endpointId, DescriptorSet: pb, Name: "test", - ServiceName: "test", + ServiceName: fmt.Sprintf("test-data-ep-%s", time.Now().Format("20060102150405")), Description: "test", Balance: string(goloadbalancer.RRBalanceType), Endpoints: []*api.EndpointMeta{ @@ -65,9 +67,43 @@ func putTest(t *testing.T) { Version: fmt.Sprintf("%d", time.Now().UnixMilli()), CreatedAt: timestamppb.New(time.Now()).AsTime().Format(time.RFC3339), UpdatedAt: timestamppb.New(time.Now()).AsTime().Format(time.RFC3339), - }) + } + serviceName = ep.ServiceName + err := repo.Put(context.Background(), ep) + c.So(err, c.ShouldBeNil) + ep.Key = snk.GenerateIDString() + err = repo.Put(context.Background(), ep) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "service name already exists") + ep2 := &api.Endpoints{ + Key: snk.GenerateIDString(), + DescriptorSet: pb, + Name: "test", + ServiceName: fmt.Sprintf("test-data-2-ep-%s", time.Now().Format("20060102150405")), + Description: "test", + Balance: string(goloadbalancer.RRBalanceType), + Endpoints: []*api.EndpointMeta{ + { + Addr: "127.0.0.1:21213", + Weight: 0, + }, + { + Addr: "127.0.0.1:21214", + Weight: 0, + }, + { + Addr: "127.0.0.1:21215", + Weight: 0, + }, + }, + Tags: []string{}, + Version: fmt.Sprintf("%d", time.Now().UnixMilli()), + CreatedAt: timestamppb.New(time.Now()).AsTime().Format(time.RFC3339), + UpdatedAt: timestamppb.New(time.Now()).AsTime().Format(time.RFC3339), + } + endpointId2 = ep2.Key + err = repo.Put(context.Background(), ep2) c.So(err, c.ShouldBeNil) - patch := gomonkey.ApplyFuncReturn((*Data).PutEtcdWithTxn, false, fmt.Errorf("put endpoint fail")) defer patch.Reset() err = repo.Put(context.Background(), &api.Endpoints{ @@ -268,11 +304,19 @@ func patchEndpointTest(t *testing.T) { c.So(updated.Tags, c.ShouldContain, tag1) keys, err := repo.GetKeysByTags(context.Background(), []string{tag}) c.So(err, c.ShouldBeNil) + t.Logf("endpointID %s", endpointId) c.So(keys, c.ShouldBeEmpty) keys, err = repo.GetKeysByTags(context.Background(), []string{tag1}) c.So(err, c.ShouldBeNil) - t.Logf("keys:%v", keys) c.So(keys, c.ShouldContain, endpointKey) + err = repo.Patch(context.Background(), endpointId2, map[string]interface{}{ + "description": "test description", + "balance": string(goloadbalancer.WRRBalanceType), + "tags": []string{tag1, tag3}, + "service_name": serviceName, + }) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "service name already exists") invalidPatch := map[string]interface{}{ "description": "test description", "balance": string(goloadbalancer.WRRBalanceType), @@ -442,9 +486,37 @@ func putTagsTest(t *testing.T) { }) } +func checkServiceNameExistsTest(t *testing.T) { + c.Convey("test check service name exists", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + repo := NewEndpointRepo(conf, gateway.Log) + patch := gomonkey.ApplyFuncReturn(json.Unmarshal, fmt.Errorf("unmarshal fail")) + defer patch.Reset() + err := repo.ServiceNameExists(context.Background(), serviceName, endpointId) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "unmarshal fail") + + patch2 := gomonkey.ApplyFuncReturn((*endpointRepoImpl).Get, "", fmt.Errorf("get endpoint fail")) + defer patch2.Reset() + err = repo.ServiceNameExists(context.Background(), serviceName, endpointId) + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(),c.ShouldContainSubstring,"get endpoint fail") + + err = repo.ServiceNameExists(context.Background(), serviceName, endpointId) + c.So(err, c.ShouldBeNil) + }) + +} func TestEndpoint(t *testing.T) { t.Run("put", putTest) + t.Run("checkServiceNameExistsTest", checkServiceNameExistsTest) t.Run("get", getEndpointTest) t.Run("getKeysByTags", getKeysByTagsTest) t.Run("list", testList) diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 90a0d1c..2226f47 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -202,7 +202,7 @@ func (c *Config) GetRPCPlugins() ([]*goloadbalancer.Server, error) { return plugins, nil } func (c *Config) GetEndpointsPrefix() string { - return fmt.Sprintf("%s%s", c.GetEnv(), c.getWithEnv("common.etcd.endpoint.prefix")) + return fmt.Sprintf("/%s%s", c.GetEnv(), c.getWithEnv("common.etcd.endpoint.prefix")) } func (c *Config) GetGatewayDescriptionOut() string { @@ -225,9 +225,13 @@ func (c *Config) GetServiceTagsPrefix() string { prefix := c.GetEndpointsPrefix() return fmt.Sprintf("%s/tags", prefix) } -func (c *Config) GetServiceKey(id string) string { - prefix := c.GetServicePrefix() - return filepath.Join(prefix, id) +func (c *Config) GetServiceKey(key string) string { + if tiga.IsSnowflakeID(key){ + prefix := c.GetServicePrefix() + return filepath.Join(prefix, key) + } + return c.GetServiceNameKey(key) + } func (c *Config) GetServiceNameKey(name string) string { prefix := c.GetServiceNamePrefix() diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index 7a71b6a..37a7328 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -9,6 +9,7 @@ import ( conf "github.com/begonia-org/begonia/config" cfg "github.com/begonia-org/begonia/internal/pkg/config" c "github.com/smartystreets/goconvey/convey" + "github.com/spark-lence/tiga" "github.com/spf13/viper" ) @@ -72,8 +73,10 @@ func TestConfig(t *testing.T) { c.So(config.GetServicePrefix(), c.ShouldEndWith, "/service") c.So(config.GetServiceNamePrefix(), c.ShouldEndWith, "/service_name") c.So(config.GetServiceTagsPrefix(), c.ShouldEndWith, "/tags") - t.Logf("service prefix: %s", config.GetServicePrefix()) - c.So(config.GetServiceKey("test"), c.ShouldStartWith, config.GetServicePrefix()) + snk,_:=tiga.NewSnowflake(1) + c.So(config.GetServiceKey("test"), c.ShouldStartWith, config.GetServiceNamePrefix()) + c.So(config.GetServiceKey(snk.GenerateIDString()), c.ShouldStartWith, config.GetServicePrefix()) + c.So(config.GetServiceNameKey("test"), c.ShouldStartWith, config.GetServiceNamePrefix()) c.So(config.GetAppKeyPrefix(), c.ShouldNotBeEmpty) c.So(config.GetAPPKey("test"), c.ShouldStartWith, config.GetAppKeyPrefix()) diff --git a/internal/service/base_test.go b/internal/service/base_test.go index 9b897b4..a8560e7 100644 --- a/internal/service/base_test.go +++ b/internal/service/base_test.go @@ -1,6 +1,7 @@ package service_test import ( + "context" "encoding/json" "log" "os" @@ -14,7 +15,10 @@ import ( "github.com/begonia-org/begonia/gateway" "github.com/begonia-org/begonia/internal" api "github.com/begonia-org/go-sdk/api/app/v1" + user "github.com/begonia-org/go-sdk/api/user/v1" example "github.com/begonia-org/go-sdk/example" + "github.com/spark-lence/tiga" + clientv3 "go.etcd.io/etcd/client/v3" ) var onceExampleServer sync.Once @@ -90,9 +94,65 @@ func RunTestServer() { func TestMain(m *testing.M) { readInitAPP() + // setup() RunTestServer() time.Sleep(5 * time.Second) m.Run() + log.Printf("All tests passed") + time.Sleep(20 * time.Second) + clean() } + +func clean() { + log.Printf("Cleaned up test data start") + + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := config.ReadConfig(env) + + // cnf:=config.NewConfig(conf) + rdb := tiga.NewRedisDao(conf) + luaScript := ` + local prefix = KEYS[1] + local cursor = "0" + local count = 100 + repeat + local result = redis.call("SCAN", cursor, "MATCH", prefix, "COUNT", count) + cursor = result[1] + local keys = result[2] + if #keys > 0 then + redis.call("DEL", unpack(keys)) + end + until cursor == "0" + return "OK" + ` + + _, err := rdb.GetClient().Eval(context.Background(), luaScript, []string{"test:*"}).Result() + if err != nil { + log.Fatalf("Could not execute Lua script: %v", err) + } + etcd := tiga.NewEtcdDao(conf) + // 设置前缀 + prefix := "/test" + + // 使用前缀删除所有键 + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + err = etcd.Delete(ctx, prefix, clientv3.WithPrefix()) + if err != nil { + log.Fatalf("Failed to delete keys with prefix %s: %v", prefix, err) + } + + mysql := tiga.NewMySQLDao(conf) + mysql.RegisterTimeSerializer() + err=mysql.GetModel(&user.Users{}).Where("`group` = ?", "test-user-01").Delete(&user.Users{}).Error + if err != nil { + log.Fatalf("Failed to delete keys with prefix %s: %v", prefix, err) + } + log.Printf("Cleaned up test data") +} diff --git a/internal/service/gateway_test.go b/internal/service/gateway_test.go index ba3d5a1..1031186 100644 --- a/internal/service/gateway_test.go +++ b/internal/service/gateway_test.go @@ -35,7 +35,7 @@ func postEndpoint(t *testing.T) { endpoint := &api.EndpointSrvConfig{ DescriptorSet: pb, Name: "test", - ServiceName: "test", + ServiceName: fmt.Sprintf("test-service-%d", time.Now().Unix()), Description: "test", Balance: string(goloadbalancer.RRBalanceType), Endpoints: []*api.EndpointMeta{ diff --git a/test.log b/test.log index 5915e2e..35d376d 100644 --- a/test.log +++ b/test.log @@ -1,18 +1,2347 @@ ? github.com/begonia-org/begonia [no test files] -? github.com/begonia-org/begonia/cmd/gateway [no test files] +? github.com/begonia-org/begonia/cmd/begonia [no test files] ? github.com/begonia-org/begonia/config [no test files] +? github.com/begonia-org/begonia/example [no test files] ? github.com/begonia-org/begonia/internal [no test files] -? github.com/begonia-org/begonia/internal/auth [no test files] ? github.com/begonia-org/begonia/internal/daemon [no test files] -? github.com/begonia-org/begonia/internal/pkg [no test files] -? github.com/begonia-org/begonia/internal/pkg/config [no test files] -? github.com/begonia-org/begonia/internal/pkg/crypto [no test files] -? github.com/begonia-org/begonia/internal/pkg/errors [no test files] -? github.com/begonia-org/begonia/internal/pkg/middleware [no test files] -? github.com/begonia-org/begonia/internal/pkg/middleware/auth [no test files] -? github.com/begonia-org/begonia/internal/pkg/migrate [no test files] -? github.com/begonia-org/begonia/internal/pkg/routers [no test files] -? github.com/begonia-org/begonia/internal/pkg/utils [no test files] +? github.com/begonia-org/begonia/internal/migrate [no test files] ? github.com/begonia-org/begonia/internal/server [no test files] +2024/06/04 02:15:40 config dir: /data/work/begonia-org/begonia/config === RUN TestRequest -FAIL github.com/begonia-org/begonia/gateway 2.678s + + test request ✔✔✔✔ + + +4 total assertions + +--- PASS: TestRequest (3.00s) +=== RUN TestUnaryInterceptor + + TestUnaryInterceptor ✔✔✔✔✔✔ + + +10 total assertions + +--- PASS: TestUnaryInterceptor (0.00s) +=== RUN TestFormDataContentType + + TestFormDataContentType ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +26 total assertions + +--- PASS: TestFormDataContentType (0.00s) +=== RUN TestFormUrlEncodedContentType + + TestFormUrlEncodedContentType ✔✔✔✔✔✔ + + +32 total assertions + +--- PASS: TestFormUrlEncodedContentType (0.00s) +=== RUN TestFormUrlEncodedErr + + TestFormUrlEncodedErr ✔✔✔✔✔ + + +37 total assertions + +--- PASS: TestFormUrlEncodedErr (0.00s) +=== RUN TestFormDataDecodeErr + + TestFormDataDecodeErr ✔✔✔✔✔✔✔✔ + + +45 total assertions + +--- PASS: TestFormDataDecodeErr (0.00s) +=== RUN TestFormDataValueErr + + test formdata value err ✔✔✔✔✔✔✔✔✔ + + +54 total assertions + +--- PASS: TestFormDataValueErr (0.00s) +=== RUN TestGrpcHandleErr + + test grpc handle err ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +69 total assertions + +--- PASS: TestGrpcHandleErr (5.01s) +=== RUN TestHttp +=== RUN TestHttp/testRegisterClient + + test register client ✔✔✔✔✔✔✔✔✔✔2024/06/04 02:15:52 Start on 127.0.0.1:7224 +✔✔✔ + + +82 total assertions + +=== RUN TestHttp/testHttp2 + + test http2 {"elapsed":"2.368332ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHello","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"","x-http-method":"","x-http-uri":"","x-request-id":"","x-uid":""} +✔ http_test.go:1188: begonia +✔ + + +84 total assertions + +=== RUN TestHttp/testRequestGet + + test request GET ✔{"elapsed":"846.913µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"12345678","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"6265d996-e9d5-4287-807a-526075a43055","x-uid":""} +✔✔✔✔✔✔✔✔✔✔{"elapsed":"725.927µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/http-code?msg=hello","x-request-id":"0b0eb2dc-5216-4f7a-ae72-4bd42852400d","x-uid":""} +✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"Method Not Allowed","status":501,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47458","x-http-method":"POST","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"5da5ec6f-bd9f-4352-99ea-43914fa88029","x-uid":""} +✔✔ + + +100 total assertions + +=== RUN TestHttp/testPanicRecover + + test panic recover ✔{"elapsed":"31.689µs","file":"/data/work/begonia-org/begonia/gateway/exception.go","fn":"github.com/begonia-org/begonia/gateway.(*Exception).handlePanic","level":"ERROR","line":56,"message":"rpc error: code = Internal desc = panic: test panic\nStack trace:\ngithub.com/begonia-org/begonia/gateway.(*Exception).StreamInterceptor.func1\n\t/data/work/begonia-org/begonia/gateway/exception.go:70\nruntime.gopanic\n\t/data/work/go/src/runtime/panic.go:884\ngithub.com/begonia-org/begonia/gateway.testPanicRecover.func1.1\n\t/data/work/begonia-org/begonia/gateway/http_test.go:1174\ngoogle.golang.org/grpc.getChainStreamHandler.func1\n\t/data/work/go/pkg/mod/google.golang.org/grpc@v1.64.0/server.go:1525\ngithub.com/begonia-org/begonia/gateway.(*Exception).StreamInterceptor\n\t/data/work/begonia-org/begonia/gateway/exception.go:73\n","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":5000,"timestamp":"2024-06-04 02:15:57","x-access-key":"12345678","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello\u0026test=panic","x-request-id":"348a3584-3613-4bba-97bc-1be5b9bce123","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/exception.go","fn":"github.com/begonia-org/begonia/gateway.(*Exception).handlePanic","level":"ERROR","line":56,"message":"panic: test panic\nStack trace:\ngithub.com/begonia-org/begonia/gateway.(*Exception).StreamInterceptor.func1\n\t/data/work/begonia-org/begonia/gateway/exception.go:70\nruntime.gopanic\n\t/data/work/go/src/runtime/panic.go:884\ngithub.com/begonia-org/begonia/gateway.testPanicRecover.func1.1\n\t/data/work/begonia-org/begonia/gateway/http_test.go:1174\ngoogle.golang.org/grpc.getChainStreamHandler.func1\n\t/data/work/go/pkg/mod/google.golang.org/grpc@v1.64.0/server.go:1525\ngithub.com/begonia-org/begonia/gateway.(*Exception).StreamInterceptor\n\t/data/work/begonia-org/begonia/gateway/exception.go:73\n","status":500,"timestamp":"2024-06-04 02:15:57","x-access-key":"12345678","x-http-forwarded-for":"127.0.0.1:47462","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello\u0026test=panic","x-request-id":"b1cd7ae1-f44d-4fa1-8645-e388f147ead0","x-uid":""} +✔✔ + + +103 total assertions + +=== RUN TestHttp/testCors + + test cors ✔✔✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":71,"message":"origin:http://www.begonia-org.com not allowed","timestamp":"2024-06-04 02:15:57"} +✔✔ + + +110 total assertions + +=== RUN TestHttp/testRequestPost + + test request POST json ✔{"elapsed":"921.786µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHello","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/post","x-request-id":"d16836b7-6c0a-4d81-a13b-63d06d7fdd28","x-uid":""} +✔✔✔✔✔✔ + + +117 total assertions + + + test request POST form-data ✔✔{"elapsed":"813.926µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHello","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/post","x-request-id":"335b65fe-c036-4e74-be37-5356a736e491","x-uid":""} +✔✔✔✔✔✔ + + +125 total assertions + + + test request POST form-urlencoded ✔{"elapsed":"1.694342ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHello","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/post","x-request-id":"609b2cba-0bbf-4719-95a3-96eec46f3f18","x-uid":""} +✔✔✔✔✔✔ + + +132 total assertions + + + test request POST binary ✔{"elapsed":"838.439µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloBody","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/body","x-request-id":"893f2c79-2b00-4fcf-b799-f7a63deddf35","x-uid":""} +✔✔✔✔✔✔✔ + + +140 total assertions + +=== RUN TestHttp/testServerSideEvent + + test server side event {"elapsed":"909.645µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloServerSideEvent","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"a8419936-43c4-4568-8733-8af20c48b932","x-uid":""} +✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +161 total assertions + +=== RUN TestHttp/testWebsocket + + test websocket ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +202 total assertions + +=== RUN TestHttp/testClientStreamRequest + + test client stream request ✔{"elapsed":"2.803112ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloWebsocket","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/websocket","x-request-id":"92e77d7a-55ac-4903-aaf2-c7639d2fc6ce","x-uid":""} +{"elapsed":"756.196µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"79a28caa-548a-4e0f-aa1d-9e64385d5de6","x-uid":""} +✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +218 total assertions + +=== RUN TestHttp/testClientStreamRequestByMarshal + + test client stream request ✔{"elapsed":"734.079µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":200,"timestamp":"2024-06-04 02:15:57","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"dd6b24d0-ecd4-4984-b06c-6f40b177cd45","x-uid":""} +✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +242 total assertions + +=== RUN TestHttp/testLoadGlobalTypes + + test load global types ✔✔✔✔✔✔✔ + + +249 total assertions + +=== RUN TestHttp/testUpdateLoadbalance + + test update loadbalance ✔✔✔✔✔✔✔✔✔✔{"elapsed":"4.025423ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"91da59de-4c41-45ac-883c-775609ede18d","x-uid":""} +{"elapsed":"1.330702ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"cd2f8390-8615-449b-9eec-e48e310bc6e9","x-uid":""} +{"elapsed":"4.441393ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"a0dc0bf8-bb8c-4ade-a9a0-d8b7109ff65f","x-uid":""} +{"elapsed":"4.801304ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"16f2c88f-c0db-4503-ac03-23c1d4fbf9fb","x-uid":""} +{"elapsed":"2.007205ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"0cf18e6d-7fa5-484f-8d61-26b42023b665","x-uid":""} +{"elapsed":"30.849991ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"88511525-b0d8-45f9-b417-9e019950d751","x-uid":""} +{"elapsed":"30.319303ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"b8839acb-d4f3-4024-973d-aafdf8237da4","x-uid":""} +{"elapsed":"28.777757ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"e88e9b2c-3f8e-4e46-9500-3cd5980b8ec1","x-uid":""} +{"elapsed":"27.76696ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"3d2bff1a-81c7-4063-bb5f-5c42d54346ce","x-uid":""} +{"elapsed":"26.90546ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"98660b30-51ab-480a-b9cd-f2503fe99f84","x-uid":""} +✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +272 total assertions + +=== RUN TestHttp/testHttpError + + test http error ✔✔2024/06/04 02:16:00 SayHelloError: ok,code:0 +{"elapsed":"914.085µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloError","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=0","x-request-id":"a80594e0-40a9-4684-a950-446d0db12c79","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:13 +{"elapsed":"859.43µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":145,"message":"rpc error: code = Internal desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":5000,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=13","x-request-id":"04428061-b0cd-4e68-998f-420d66e9e8e4","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":145,"message":"ok","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47570","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=13","x-request-id":"4835e246-186b-40c6-bb7a-339ae6864e17","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:3 +{"elapsed":"712.222µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":142,"message":"rpc error: code = InvalidArgument desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":4000,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=3","x-request-id":"47024291-c101-4387-8a55-b76d8ddf9063","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":142,"message":"ok","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47572","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=3","x-request-id":"f00421c6-f47f-4296-b53f-74c9ca56c26b","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:5 +{"elapsed":"711.575µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":147,"message":"rpc error: code = NotFound desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":4004,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=5","x-request-id":"5720bc6c-b39d-4207-a8e0-c1121b57b98a","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":147,"message":"ok","status":404,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47586","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=5","x-request-id":"0c60c495-98d5-499a-bac5-ea01cf3aa4eb","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:7 +{"elapsed":"892.216µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":149,"message":"rpc error: code = PermissionDenied desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":4001,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=7","x-request-id":"b0259373-6da0-40f3-94c6-87cb1d4c93ba","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":149,"message":"ok","status":403,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47592","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=7","x-request-id":"e3645b50-9b60-4cbf-875c-f8dc7da6d0a0","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:16 +{"elapsed":"708.39µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":151,"message":"rpc error: code = Unauthenticated desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":4001,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=16","x-request-id":"bd86e53d-6e6f-4e96-8aab-5e74c2290762","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":151,"message":"ok","status":401,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47594","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=16","x-request-id":"c8be4eb9-4df1-496f-b575-3240e17a64f8","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:8 +{"elapsed":"698.568µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":161,"message":"rpc error: code = ResourceExhausted desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":4008,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=8","x-request-id":"348e239e-e9a5-4c55-8a11-4beb59e6e070","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":161,"message":"ok","status":429,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47606","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=8","x-request-id":"6ceae848-4cbf-42a4-807c-8128fe00113b","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:4 +{"elapsed":"705.665µs","file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":159,"message":"rpc error: code = DeadlineExceeded desc = ok","module":"request","name":"/helloworld.Greeter/SayHelloError","status":5004,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=4","x-request-id":"6b82a5dc-957f-4ae1-9748-f70ae79b39b1","x-uid":""} +{"file":"/data/work/begonia-org/begonia-go-sdk/example/server.go","fn":"github.com/begonia-org/go-sdk/example.(*server).SayHelloError","level":"ERROR","line":159,"message":"ok","status":504,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47622","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=4","x-request-id":"8e17ae2e-a21b-40b2-ab27-8c8bc1dd2549","x-uid":""} +✔✔✔✔2024/06/04 02:16:00 SayHelloError: ok,code:99999 +{"elapsed":"775.275µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloError","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=99999","x-request-id":"13a31d72-6999-470e-bd29-160c13cca083","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"HttpResponseBodyModify","level":"ERROR","line":336,"message":"strconv.ParseInt: parsing \"test\": invalid syntax","timestamp":"2024-06-04 02:16:00"} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"strconv.ParseInt: parsing \"test\": invalid syntax","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47634","x-http-method":"GET","x-http-uri":"/api/v1/example/error/test?msg=ok\u0026code=99999","x-request-id":"3e5998ab-51af-455f-8192-12e0144fc10e","x-uid":""} +✔✔ + + +308 total assertions + +=== RUN TestHttp/testWebSocketError + + test websocket error ✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":465,"message":"Failed to start websocket request: Failed to start websocket request: test stream error","timestamp":"2024-06-04 02:16:00"} +✔✔✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":465,"message":"Failed to start websocket request: Failed to get header from websocket client: test header error","timestamp":"2024-06-04 02:16:00"} +{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func2","level":"WARNING","line":182,"message":"Failed to send websocket request: read tcp 127.0.0.1:7224-\u003e127.0.0.1:47650: use of closed network connection","timestamp":"2024-06-04 02:16:00"} +✔✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func2","level":"WARNING","line":182,"message":"Failed to send websocket request: test send error","timestamp":"2024-06-04 02:16:00"} +✔✔✔✔{"elapsed":"687.572µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Canceled desc = context canceled","module":"request","name":"/helloworld.Greeter/SayHelloWebsocket","status":499,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/websocket","x-request-id":"9a9abca5-9592-4ea2-8c62-d3861785a475","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func2","level":"WARNING","line":182,"message":"Failed to send websocket request: Failed to decode websocket request: test BIN DECODE error","timestamp":"2024-06-04 02:16:00"} +✔✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func2","level":"WARNING","line":182,"message":"Failed to send websocket request: Failed to send request: test send error","timestamp":"2024-06-04 02:16:00"} +✔✔{"elapsed":"944.097µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloWebsocket","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/websocket","x-request-id":"6c1fbed3-bd82-4457-b5eb-5404db747046","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"ERROR","line":456,"message":"Failed to upgrade to websocket: test upgrade error","timestamp":"2024-06-04 02:16:00"} +✔{"elapsed":"1.25301ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloWebsocket","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/websocket","x-request-id":"59022adc-bf39-4dcf-a6cf-610705c8221c","x-uid":""} +✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":465,"message":"Failed to start websocket request: Failed to start websocket request: test get conn error","timestamp":"2024-06-04 02:16:00"} +✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":465,"message":"Failed to start websocket request: Failed to start websocket request: test new stream error","timestamp":"2024-06-04 02:16:00"} +✔{"elapsed":"1.57092ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"test new stream error","module":"request","name":"/helloworld.Greeter/SayHelloWebsocket","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/websocket","x-request-id":"40e2aeeb-3f1f-404f-bdf4-40a91e35c389","x-uid":""} +✔✔✔2024/06/04 02:16:00 http: response.WriteHeader on hijacked connection from github.com/grpc-ecosystem/grpc-gateway/v2/runtime.handleForwardResponseStreamError (handler.go:208) + + + +337 total assertions + +=== RUN TestHttp/testServerSideEventErr + + test server side event error {"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to add request params: test inParamsHandle error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47716","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"2e7a86ff-0319-4463-8b97-35e5ce8b80f4","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to start streaming: test ServerSideStream error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47716","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"c22a4623-1265-4bb2-88f9-7fc8f7ef90c6","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to get header from client: test header error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47716","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"3be281f7-0ef4-4259-bb0c-d5dcb72fb4c6","x-uid":""} +✔{"elapsed":"26.542µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Unavailable desc = no endpoint available from endpoint,context canceled","module":"request","name":"/helloworld.Greeter/SayHelloServerSideEvent","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"44cf0cf1-8284-41a6-ab3b-5e77f94225ad","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to start streaming: test get conn error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47716","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"548413f0-3e1b-42fa-a389-b2c90699f74c","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to start streaming: test new stream error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47716","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"93ff0516-7139-4ab2-a4ce-b4ba32670fe6","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to start streaming: test send error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47716","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"4ad68a2b-46da-4894-8242-6dd03bf1f03d","x-uid":""} +✔{"elapsed":"664.572µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Canceled desc = context canceled","module":"request","name":"/helloworld.Greeter/SayHelloServerSideEvent","status":499,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"5f9d167d-71a9-4788-ba98-fb901cfe2a42","x-uid":""} +{"elapsed":"812.605µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloServerSideEvent","status":200,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"7570ed94-c899-4529-8005-5d4bbe400925","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"test decode error","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47724","x-http-method":"GET","x-http-uri":"/api/v1/example/server/sse/world?msg=hello\u0026stream=message","x-request-id":"2baa2254-b7bc-4a4d-91e4-d37e484c10b1","x-uid":""} +✔ + + +345 total assertions + +=== RUN TestHttp/testClientStreamErr + + test client stream error ✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*errors.errorString, error:test ClientSideStream error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"e693d848-bc15-4a1a-800f-289f78a72ea4","x-uid":""} +✔✔ http_test.go:977: data: +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"Failed to decode request:test NewDecoder error","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"fe318924-bd69-4f37-ab5a-038a9e9bfff8","x-uid":""} +✔✔ http_test.go:977: data:{"message":"Unknown error","code":5000} +{"elapsed":"22.554µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Unavailable desc = no endpoint available from endpoint,context canceled","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"0d278dbf-11b6-48fe-92ba-23ba86083079","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"Failed to add request params:test inParamsHandle error","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"62f8938c-cceb-4191-a433-6a4b15954d09","x-uid":""} +✔✔ http_test.go:977: data:{"message":"Unknown error","code":5000} +{"elapsed":"19.076µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Unavailable desc = no endpoint available from endpoint,context canceled","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"9f1b4673-c27e-4e56-9ff0-0edb50ff8a50","x-uid":""} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"Failed to send request:test client stream send error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"17a59bff-1449-41c8-93bb-0e1d057c5cfa","x-uid":""} +✔✔ http_test.go:977: data:{"message":"Unknown error","code":5000} +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to close send: test client stream close error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"893133ae-0c0e-4e46-83a4-9629383d79a0","x-uid":""} +✔✔ http_test.go:977: data: +✔{"elapsed":"21.594µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Unavailable desc = no endpoint available from endpoint,context canceled","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"b0011927-9504-4d15-9a2a-55dec01703bf","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to get header from client: test header error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"85aea5d0-dbdb-4f23-891d-6ad9d9e44fe6","x-uid":""} +{"elapsed":"617.878µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Canceled desc = context canceled","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":499,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"53f34756-ae98-462a-91c4-2a0bcf968c8b","x-uid":""} +✔✔ http_test.go:977: data: +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*errors.errorString, error:test get conn error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"e194db5c-59d2-4bbe-9cd5-3e2faeb03531","x-uid":""} +✔✔ http_test.go:977: data: +✔{"elapsed":"536.343µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"test new stream error","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"30217c83-e641-46ba-86c9-73ffe8d46c10","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*errors.errorString, error:test new stream error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"f9383afd-dc8b-4dd9-a749-955db333a7b6","x-uid":""} +✔✔ http_test.go:977: data: +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"Failed to decode request:failed to unmarshal message from HttpProtobufStreamImpl: test unmarshal error","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"ae63859b-0626-42a2-ab54-b599f0243e37","x-uid":""} +✔✔ http_test.go:977: data:{"message":"Unknown error","code":5000} + + + +372 total assertions + +=== RUN TestHttp/testAddHexEncodeSHA256HashV2Err + + test add hex encode sha256 hash v2 err ✔✔✔ + + +375 total assertions + +=== RUN TestHttp/testRegisterHandlerClientErr + + test register handler client err ✔{"elapsed":"621.495µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Canceled desc = context canceled","module":"request","name":"/helloworld.Greeter/SayHelloClientStream","status":499,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/example/client/stream","x-request-id":"afdeef7b-a82e-4537-ba42-23b18a1f669b","x-uid":""} +✔✔✔ + + +379 total assertions + +=== RUN TestHttp/testRequestError + + test request error http_test.go:661: test error test case:0 +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"decode request body err test json decode error","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47732","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"e4086938-e2f5-44bb-a2b5-1954a9fea5d2","x-uid":""} +✔✔ http_test.go:661: test error test case:1 +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"Failed to add request params: test inParamsHandle error","status":400,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47744","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"1070b831-c779-40bd-8f96-8125bac78124","x-uid":""} +✔✔ http_test.go:661: test error test case:2 +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*fmt.wrapError, error:Failed to add sha256 hash: test io copy error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47754","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"0377b9c9-eec9-46aa-bfaf-e44b897dd925","x-uid":""} +✔✔ http_test.go:661: test error test case:3 +✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":288,"message":"error type:*errors.errorString, error:test AnnotateContext error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47754","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"b0f3c28c-ee36-465f-ae4f-55f28a515496","x-uid":""} +✔✔ http_test.go:661: test error test case:4 +✔{"elapsed":"13.056µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Internal desc = stream not exists in context","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"cedc6e8c-6444-4d4e-8495-5bc2024df0a5","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"stream not exists in context","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47754","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"d69357a4-3438-458c-ac58-0dcd6be2d7b4","x-uid":""} +✔✔ http_test.go:661: test error test case:5 +✔{"elapsed":"19.585µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Unavailable desc = no source ip","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"d0a10720-3a11-44b7-806b-d7f3766abeef","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"no source ip","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47760","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"0dec3741-1b95-42ba-91e2-e1acad878b04","x-uid":""} +✔✔ http_test.go:661: test error test case:6 +✔{"elapsed":"47.929µs","file":"/data/work/begonia-org/begonia/gateway/exception.go","fn":"github.com/begonia-org/begonia/gateway.(*Exception).handlePanic","level":"ERROR","line":56,"message":"rpc error: code = Internal desc = panic: reflect.Set: value of type *errors.errorString is not assignable to type []string\nStack trace:\ngithub.com/begonia-org/begonia/gateway.(*Exception).StreamInterceptor.func1\n\t/data/work/begonia-org/begonia/gateway/exception.go:70\nruntime.gopanic\n\t/data/work/go/src/runtime/panic.go:884\nreflect.Value.assignTo\n\t/data/work/go/src/reflect/value.go:3245\nreflect.Value.Set\n\t/data/work/go/src/reflect/value.go:2236\ngithub.com/agiledragon/gomonkey/v2.GetResultValues\n\t/data/work/go/pkg/mod/github.com/agiledragon/gomonkey/v2@v2.11.0/patch.go:320\n","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":5000,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"c71b81f1-ddf4-4131-a5a5-fab4f3117acb","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/exception.go","fn":"github.com/begonia-org/begonia/gateway.(*Exception).handlePanic","level":"ERROR","line":56,"message":"panic: reflect.Set: value of type *errors.errorString is not assignable to type []string\nStack trace:\ngithub.com/begonia-org/begonia/gateway.(*Exception).StreamInterceptor.func1\n\t/data/work/begonia-org/begonia/gateway/exception.go:70\nruntime.gopanic\n\t/data/work/go/src/runtime/panic.go:884\nreflect.Value.assignTo\n\t/data/work/go/src/reflect/value.go:3245\nreflect.Value.Set\n\t/data/work/go/src/reflect/value.go:2236\ngithub.com/agiledragon/gomonkey/v2.GetResultValues\n\t/data/work/go/pkg/mod/github.com/agiledragon/gomonkey/v2@v2.11.0/patch.go:320\n","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47776","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"075aeeb5-d8d7-402c-ac1f-fa53af8d9b5d","x-uid":""} +✔✔ http_test.go:661: test error test case:7 +✔{"elapsed":"19.725µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Unavailable desc = no endpoint available to select,test select error","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"c1af2934-3f46-48ff-b9f4-9d98f20210e3","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"no endpoint available to select,test select error","status":503,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47788","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"b15d37d2-fcc1-4915-8e28-3b873bd62954","x-uid":""} +✔✔ http_test.go:661: test error test case:8 +✔{"elapsed":"586.779µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"rpc error: code = Internal desc = failed proxying s2c: test forwardServerToClient error","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"d529dbfc-1a8b-4ea0-a477-a1548925eaff","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"failed proxying s2c: test forwardServerToClient error","status":500,"timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1:47804","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"36e1f34c-fbc2-4a7d-8df0-c73e9a3cac0f","x-uid":""} +✔✔ + + +406 total assertions + +=== RUN TestHttp/testLoadHttpEndpointItemErr + + test load http endpoint item ✔✔2024/06/04 02:16:00 config dir: /data/work/begonia-org/begonia/config + http_test.go:1021: err test case:0 +✔2024/06/04 02:16:00 config dir: /data/work/begonia-org/begonia/config + http_test.go:1021: err test case:1 +✔2024/06/04 02:16:00 config dir: /data/work/begonia-org/begonia/config + http_test.go:1021: err test case:2 +✔ + + +411 total assertions + +=== RUN TestHttp/testDeleteEndpoint + + test delete endpoint ✔✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":363,"message":"not found router","timestamp":"2024-06-04 02:16:00","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"f262f8f7-9c06-4181-9e60-c4841fe40ff4","x-uid":""} +✔✔✔✔✔ + + +420 total assertions + +=== RUN TestHttp/testRegisterLocalService + + test register local service ✔✔2024/06/04 02:16:00 config dir: /data/work/begonia-org/begonia/config +✔2024/06/04 02:16:01 Start on 127.0.0.1:6054 +✔{"elapsed":"20.786µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:16:03","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"a3632239-1584-448d-80df-770ca1b9fc72","x-uid":""} +✔✔✔✔✔✔✔✔ + + +432 total assertions + + + test del local service ✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":363,"message":"not found router","timestamp":"2024-06-04 02:16:03","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/world?msg=hello","x-request-id":"9715d90e-9ad4-4e01-a494-4bd097bf60d3","x-uid":""} +✔✔ + + +435 total assertions + +=== RUN TestHttp/testStartErr + + test start error ✔✔2024/06/04 02:16:03 config dir: /data/work/begonia-org/begonia/config +✔✔2024/06/04 02:16:03 config dir: /data/work/begonia-org/begonia/config +✔2024/06/04 02:16:04 Start on 127.0.0.1:8566 +2024/06/04 02:16:06 config dir: /data/work/begonia-org/begonia/config +✔2024/06/04 02:16:07 Start on 127.0.0.1:7224 +✔ + + +442 total assertions + +--- PASS: TestHttp (18.18s) + --- PASS: TestHttp/testRegisterClient (8.01s) + --- PASS: TestHttp/testHttp2 (0.00s) + --- PASS: TestHttp/testRequestGet (0.00s) + --- PASS: TestHttp/testPanicRecover (0.00s) + --- PASS: TestHttp/testCors (0.00s) + --- PASS: TestHttp/testRequestPost (0.01s) + --- PASS: TestHttp/testServerSideEvent (0.00s) + --- PASS: TestHttp/testWebsocket (0.00s) + --- PASS: TestHttp/testClientStreamRequest (0.00s) + --- PASS: TestHttp/testClientStreamRequestByMarshal (0.00s) + --- PASS: TestHttp/testLoadGlobalTypes (0.02s) + --- PASS: TestHttp/testUpdateLoadbalance (3.05s) + --- PASS: TestHttp/testHttpError (0.01s) + --- PASS: TestHttp/testWebSocketError (0.00s) + --- PASS: TestHttp/testServerSideEventErr (0.00s) + --- PASS: TestHttp/testClientStreamErr (0.00s) + --- PASS: TestHttp/testAddHexEncodeSHA256HashV2Err (0.00s) + --- PASS: TestHttp/testRegisterHandlerClientErr (0.01s) + --- PASS: TestHttp/testRequestError (0.00s) + --- PASS: TestHttp/testLoadHttpEndpointItemErr (0.01s) + --- PASS: TestHttp/testDeleteEndpoint (0.01s) + --- PASS: TestHttp/testRegisterLocalService (3.01s) + --- PASS: TestHttp/testStartErr (4.01s) +=== RUN TestLog + + TestLog {"file":"/data/work/begonia-org/begonia/gateway/logger_test.go","fn":"func1","level":"INFO","line":14,"message":"info","timestamp":"2024-06-04 02:16:07"} +{"file":"/data/work/begonia-org/begonia/gateway/logger_test.go","fn":"func1","level":"WARNING","line":15,"message":"warn","timestamp":"2024-06-04 02:16:07"} +{"file":"/data/work/begonia-org/begonia/gateway/logger_test.go","fn":"func1","level":"INFO","line":16,"message":"infof","timestamp":"2024-06-04 02:16:07"} +{"file":"/data/work/begonia-org/begonia/gateway/logger_test.go","fn":"func1","level":"INFO","line":19,"message":"infof","timestamp":"2024-06-04 02:16:07","x-access-key":"","x-http-forwarded-for":"","x-http-method":"","x-http-uri":"","x-request-id":"","x-uid":""} +{"file":"/data/work/go/pkg/mod/github.com/smartystreets/goconvey@v1.8.1/convey/discovery.go","fn":"func1","level":"INFO","line":89,"message":"info","timestamp":"2024-06-04 02:16:07"} +✔✔ + + +444 total assertions + +--- PASS: TestLog (0.00s) +=== RUN TestMask + + TestMask ✔✔✔✔✔✔✔✔✔ + + +453 total assertions + +--- PASS: TestMask (0.00s) +=== RUN TestDecodeErr + + TestDecodeErr ✔✔✔✔✔✔ + + +459 total assertions + +--- PASS: TestDecodeErr (0.00s) +=== RUN TestProtobufDescriptionErr +=== RUN TestProtobufDescriptionErr/Test_initDescriptorSet_error + + Test initDescriptorSet error ✔✔✔ + + +462 total assertions + +=== RUN TestProtobufDescriptionErr/Test_SetHttpResponse_error + + Test SetHttpResponse error ✔✔✔✔✔✔✔✔ + + +470 total assertions + +=== RUN TestProtobufDescriptionErr/Test_NewDescription_error + + Test NewDescription error ✔✔✔✔✔✔✔✔✔✔ + + +480 total assertions + +=== RUN TestProtobufDescriptionErr/Test_NewDescriptionFromBinary_error + + Test NewDescriptionFromBinary error ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +495 total assertions + +--- PASS: TestProtobufDescriptionErr (0.10s) + --- PASS: TestProtobufDescriptionErr/Test_initDescriptorSet_error (0.01s) + --- PASS: TestProtobufDescriptionErr/Test_SetHttpResponse_error (0.02s) + --- PASS: TestProtobufDescriptionErr/Test_NewDescription_error (0.06s) + --- PASS: TestProtobufDescriptionErr/Test_NewDescriptionFromBinary_error (0.02s) +=== RUN TestRawBinaryUnmarshaler + + TestRawBinaryUnmarshaler ✔✔✔✔✔✔✔✔✔✔✔ + + +506 total assertions + +--- PASS: TestRawBinaryUnmarshaler (0.00s) +=== RUN TestRawBinaryDecodeErr + + TestRawBinaryDecodeErr ✔✔✔✔✔✔ + + +512 total assertions + +--- PASS: TestRawBinaryDecodeErr (0.00s) +=== RUN TestClientSideStreamClient + + test clientSideStreamClient ✔✔✔✔ + + +516 total assertions + +--- PASS: TestClientSideStreamClient (0.00s) +=== RUN TestBuildGrpcRequest + + TestBuildGrpcRequest ✔✔✔✔✔✔✔✔✔ + + +525 total assertions + +--- PASS: TestBuildGrpcRequest (0.00s) +=== RUN TestNewEndpoint + + TestNewEndpoint ✔✔✔✔✔✔✔✔ + + +533 total assertions + +--- PASS: TestNewEndpoint (0.00s) +=== RUN TestJSONCamelCase + + TestJSONCamelCase ✔✔✔✔ + + +537 total assertions + +--- PASS: TestJSONCamelCase (0.00s) +PASS + github.com/begonia-org/begonia/gateway coverage: 97.6% of statements +ok github.com/begonia-org/begonia/gateway 26.419s coverage: 97.6% of statements +2024/06/04 02:15:41 Start testing +=== RUN TestAKSK +=== RUN TestAKSK/get_secret +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + get secret ✔✔ + + +2 total assertions + + + get secret fail {"file":"/data/work/begonia-org/go-layered-cache/kv.go","fn":"Get","level":"ERROR","line":103,"message":"local.Get test:begonia:kv:app:access_key:ddddeeeeeedede error:Entry not found","timestamp":"2024-06-04 02:15:42"} +✔ + + +3 total assertions + +=== RUN TestAKSK/get_appid +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + get appid ✔✔ + + +5 total assertions + + + get appid fail {"file":"/data/work/begonia-org/go-layered-cache/kv.go","fn":"Get","level":"ERROR","line":103,"message":"local.Get test:begonia:kv:app:appid:dddddeeedwecccwcqdq error:Entry not found","timestamp":"2024-06-04 02:15:42"} +✔✔ + + +7 total assertions + +=== RUN TestAKSK/validator + + test validator success ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +11 total assertions + + + test validator fail with missing key params ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔✔ + + +23 total assertions + + + test validator fail with invalidate sk ak ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +29 total assertions + +=== RUN TestAKSK/if_need_validate + + test if need validate ✔✔ + + +31 total assertions + +--- PASS: TestAKSK (1.39s) + --- PASS: TestAKSK/get_secret (1.38s) + --- PASS: TestAKSK/get_appid (0.00s) + --- PASS: TestAKSK/validator (0.00s) + --- PASS: TestAKSK/if_need_validate (0.00s) +=== RUN TestAppBiz +=== RUN TestAppBiz/testPutApp +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app put success ✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +34 total assertions + + + test app put failed ✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +47 total assertions + +=== RUN TestAppBiz/testGetApp +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app get success ✔✔✔✔✔✔ + + +53 total assertions + + + test app get failed ✔✔ + + +55 total assertions + +=== RUN TestAppBiz/testPatchApp +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app patch success ✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +68 total assertions + + + test app patch failed ✔✔✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +74 total assertions + +=== RUN TestAppBiz/testListApp +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app list success ✔✔✔ + + +77 total assertions + + + test app list failed ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +81 total assertions + +=== RUN TestAppBiz/testCache +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app cache success ✔ + + +82 total assertions + +=== RUN TestAppBiz/testDelApp +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app del success ✔✔✔ + + +85 total assertions + + + test app del failed ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +89 total assertions + +--- PASS: TestAppBiz (0.05s) + --- PASS: TestAppBiz/testPutApp (0.02s) + --- PASS: TestAppBiz/testGetApp (0.00s) + --- PASS: TestAppBiz/testPatchApp (0.01s) + --- PASS: TestAppBiz/testListApp (0.01s) + --- PASS: TestAppBiz/testCache (0.00s) + --- PASS: TestAppBiz/testDelApp (0.01s) +=== RUN TestAuthz +=== RUN TestAuthz/test_auth_seed +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test auth seed ✔✔✔ + + +92 total assertions + +=== RUN TestAuthz/test_login +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test login success ✔✔✔ + + +95 total assertions + + + test login failed with invalid password ✔✔✔✔ + + +99 total assertions + + + test login failed with invalid account ✔✔✔✔ + + +103 total assertions + + + test login failed with invalid pub key ✔✔✔ + + +106 total assertions + + + test login failed with invalid seed token expiration ✔✔✔✔ + + +110 total assertions + + + test login failed with invalid UserAuth ✔✔ + + +112 total assertions + + + test login failed with invalid EncryptAES ✔✔ + + +114 total assertions + + + test login failed with invalid user status ✔✔✔ + + +117 total assertions + + + test login failed with jwt generate error ✔✔ + + +119 total assertions + +=== RUN TestAuthz/test_logout +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test logout success ✔✔✔ + + +122 total assertions + + + test logout fail ✔✔✔✔✔✔ + + +128 total assertions + +=== RUN TestAuthz/test_del_token + + test del token 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔ + + +129 total assertions + +=== RUN TestAuthz/test_put_black_list +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test put black list ✔✔✔ + + +132 total assertions + +--- PASS: TestAuthz (0.28s) + --- PASS: TestAuthz/test_auth_seed (0.00s) + --- PASS: TestAuthz/test_login (0.27s) + --- PASS: TestAuthz/test_logout (0.00s) + --- PASS: TestAuthz/test_del_token (0.00s) + --- PASS: TestAuthz/test_put_black_list (0.00s) +=== RUN TestDo +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test data operator do success ✔✔✔{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"Do","level":"INFO","line":62,"message":"start watch","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"Do","level":"INFO","line":62,"message":"start watch","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:42"} +2024/06/04 02:15:42 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:42"} +2024/06/04 02:15:42 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:42"} +2024/06/04 02:15:42 lastUpdate:0001-01-01 00:00:00 +0000 UTC +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"loadUsersBlacklist","level":"INFO","line":134,"message":"load users:1","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":123,"message":"unlock success","timestamp":"2024-06-04 02:15:42"} +2024/06/04 02:15:44 lastUpdate:2024-06-04 02:15:44.202 +0800 CST +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"loadUsersBlacklist","level":"INFO","line":134,"message":"load users:1","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":123,"message":"unlock success","timestamp":"2024-06-04 02:15:44"} +✔✔✔✔{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"Do","level":"INFO","line":62,"message":"start watch","timestamp":"2024-06-04 02:15:47"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"ERROR","line":55,"message":"test data operator on start error","timestamp":"2024-06-04 02:15:47"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"ERROR","line":58,"message":"test data watcher list error","timestamp":"2024-06-04 02:15:47"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:47"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:47"} +2024/06/04 02:15:47 lockKey:test:begonia:user:black:lock +2024/06/04 02:15:47 lastUpdate:2024-06-04 02:15:44.904 +0800 CST +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":123,"message":"unlock success","timestamp":"2024-06-04 02:15:47"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:48"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"expiration time is too short","timestamp":"2024-06-04 02:15:48"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:48"} + + + +139 total assertions + +--- PASS: TestDo (7.02s) +=== RUN TestHandleError +2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config + + test data operator handle error 2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:49"} +2024/06/04 02:15:49 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:49"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"get lock error:test new lock error","timestamp":"2024-06-04 02:15:49"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:50"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:50"} +2024/06/04 02:15:50 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"lock error: test lock error","timestamp":"2024-06-04 02:15:50"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:51"} +2024/06/04 02:15:51 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:51"} +2024/06/04 02:15:51 lastUpdate:0001-01-01 00:00:00 +0000 UTC +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"loadUsersBlacklist","level":"INFO","line":134,"message":"load users:1","timestamp":"2024-06-04 02:15:51"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"ERROR","line":121,"message":"unlock error: test unlock error","timestamp":"2024-06-04 02:15:51"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:52"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:52"} +2024/06/04 02:15:52 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:53"} +2024/06/04 02:15:53 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:53"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"lock error: context canceled","timestamp":"2024-06-04 02:15:53"} + + + +139 total assertions + +--- PASS: TestHandleError (5.01s) +=== RUN TestOnStart +2024/06/04 02:15:54 config dir: /data/work/begonia-org/begonia/config +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"lock error: context canceled","timestamp":"2024-06-04 02:15:54"} + + test data operator on start 2024/06/04 02:15:54 config dir: /data/work/begonia-org/begonia/config +✔✔{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"OnStart","level":"ERROR","line":178,"message":"init endpoints error,test endpoint watcher update error","timestamp":"2024-06-04 02:15:58"} +✔✔ + + +143 total assertions + +--- PASS: TestOnStart (6.00s) +=== RUN TestRefresh + + test data operator refresh 2024/06/04 02:16:00 config dir: /data/work/begonia-org/begonia/config +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:01"} +2024/06/04 02:16:01 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:01"} +2024/06/04 02:16:01 lastUpdate:2024-06-04 02:15:51.909 +0800 CST +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":123,"message":"unlock success","timestamp":"2024-06-04 02:16:01"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:02"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:02"} +2024/06/04 02:16:02 lockKey:test:begonia:user:black:lock +2024/06/04 02:16:02 lastUpdate:2024-06-04 02:15:51.909 +0800 CST +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":123,"message":"unlock success","timestamp":"2024-06-04 02:16:02"} + + + +143 total assertions + +--- PASS: TestRefresh (3.00s) +=== RUN TestUser +=== RUN TestUser/testPutUser +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:03"} +2024/06/04 02:16:03 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:16:03 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:03"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"lock error: context canceled","timestamp":"2024-06-04 02:16:03"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"list app failed: context canceled","timestamp":"2024-06-04 02:16:03"} + + test user put success ✔{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:04"} +2024/06/04 02:16:04 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:04"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"lock error: context canceled","timestamp":"2024-06-04 02:16:04"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"list app failed: context canceled","timestamp":"2024-06-04 02:16:04"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:05"} +2024/06/04 02:16:05 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:16:05"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"lock error: context canceled","timestamp":"2024-06-04 02:16:05"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func2","level":"ERROR","line":89,"message":"list app failed: context canceled","timestamp":"2024-06-04 02:16:05"} +✔ + + +145 total assertions + + + test user put failed ✔✔2024/06/04 02:16:05 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +149 total assertions + +=== RUN TestUser/testGetUser +2024/06/04 02:16:05 config dir: /data/work/begonia-org/begonia/config + + test user get success ✔✔ + + +151 total assertions + + + test user get failed ✔✔ + + +153 total assertions + +=== RUN TestUser/testPatchUser +2024/06/04 02:16:05 config dir: /data/work/begonia-org/begonia/config + + test user patch success ✔✔ + + +155 total assertions + + + test user patch failed ✔✔✔✔✔2024/06/04 02:16:05 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +162 total assertions + +=== RUN TestUser/testListUser +2024/06/04 02:16:05 config dir: /data/work/begonia-org/begonia/config + + test user list success ✔✔ + + +164 total assertions + + + test user list failed ✔✔2024/06/04 02:16:06 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +170 total assertions + +=== RUN TestUser/testDeleteUser +2024/06/04 02:16:06 config dir: /data/work/begonia-org/begonia/config + + test user delete success ✔ + + +171 total assertions + + + test user delete failed ✔✔2024/06/04 02:16:06 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +175 total assertions + +--- PASS: TestUser (2.14s) + --- PASS: TestUser/testPutUser (2.03s) + --- PASS: TestUser/testGetUser (0.00s) + --- PASS: TestUser/testPatchUser (0.01s) + --- PASS: TestUser/testListUser (0.09s) + --- PASS: TestUser/testDeleteUser (0.01s) +PASS + github.com/begonia-org/begonia/internal/biz coverage: 98.7% of statements +2024/06/04 02:16:06 All tests passed with code 0 +ok github.com/begonia-org/begonia/internal/biz 25.102s coverage: 98.7% of statements +=== RUN TestEndpoint +=== RUN TestEndpoint/Test_Add_Endpoint +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test Add Endpoint ✔✔✔ + + +3 total assertions + + + Test Add Endpoint Fail ✔✔✔✔✔✔ + + +9 total assertions + +=== RUN TestEndpoint/Test_Watcher_Update +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test Watcher Update ✔✔ + + +11 total assertions + + + test watcher update fail ✔✔{"file":"/data/work/begonia-org/begonia/internal/biz/endpoint/watcher.go","fn":"Update","level":"ERROR","line":48,"message":"get descriptor set error: rpc error: code = Internal desc = Failed to register: no gateway content","timestamp":"2024-06-04 02:15:41"} +✔✔✔✔✔✔✔✔✔ + + +22 total assertions + +=== RUN TestEndpoint/Test_Get_Endpoint +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test Get Endpoint Fail ✔✔✔✔✔✔✔ + + +29 total assertions + + + Test Get Endpoint ✔✔✔✔ + + +33 total assertions + +=== RUN TestEndpoint/Test_Patch_Endpoint +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test Patch Endpoint ✔✔✔✔✔✔✔ + + +40 total assertions + + + Test Patch Endpoint Fail ✔✔✔✔✔✔✔✔✔✔✔✔ + + +52 total assertions + +=== RUN TestEndpoint/Test_List_Endpoint +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test List Endpoint ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +78 total assertions + + + Test List Endpoint Fail ✔✔✔✔ + + +82 total assertions + +=== RUN TestEndpoint/Test_Watcher_Del +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test Watcher Del ✔✔ + + +84 total assertions + + + test watcher del fail ✔✔✔✔✔✔✔ + + +91 total assertions + +=== RUN TestEndpoint/Test_Del_Endpoint +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + Test Del Endpoint ✔ + + +92 total assertions + + + Test Del Endpoint Fail ✔✔✔✔ + + +96 total assertions + +--- PASS: TestEndpoint (0.41s) + --- PASS: TestEndpoint/Test_Add_Endpoint (0.02s) + --- PASS: TestEndpoint/Test_Watcher_Update (0.12s) + --- PASS: TestEndpoint/Test_Get_Endpoint (0.00s) + --- PASS: TestEndpoint/Test_Patch_Endpoint (0.03s) + --- PASS: TestEndpoint/Test_List_Endpoint (0.19s) + --- PASS: TestEndpoint/Test_Watcher_Del (0.04s) + --- PASS: TestEndpoint/Test_Del_Endpoint (0.02s) +PASS + github.com/begonia-org/begonia/internal/biz/endpoint coverage: 98.7% of statements +ok github.com/begonia-org/begonia/internal/biz/endpoint 0.551s coverage: 98.7% of statements +=== RUN TestFile +=== RUN TestFile/test_upload +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + test upload without version ✔✔✔✔✔ + + +5 total assertions + + + test upload with version ✔✔✔✔✔✔ + + +11 total assertions + + + test upload with version1 ✔✔✔✔✔✔ + + +17 total assertions + + + test upload with version2 ✔✔✔✔✔✔ + + +23 total assertions + + + test upload with version3 ✔✔✔✔✔✔ + + +29 total assertions + + + test upload with invalid key ✔✔ + + +31 total assertions + + + test upload with invalid author ✔✔ + + +33 total assertions + + + test upload fail with not match sha256 ✔✔ + + +35 total assertions + + + test upload fail file_test.go:286: version:db64f957b53a3e214df71be460bd40d5db3e36d1,sha256:97cf7179f7eeaad7ed967dd9187bf4f1883c094f0ff9a62540377334fbccb57d + file_test.go:286: version:6c5871c69a4d8ad21ee72282555d335938d766ad,sha256:e5f3f8a4f859807cb9d6bacd2c0a775e1e4a6f2f12637367feb65a695cbefd80 + file_test.go:286: version:424a3bea930c6046ed0129ee92fb84e59355e7d9,sha256:97cf7179f7eeaad7ed967dd9187bf4f1883c094f0ff9a62540377334fbccb57d + file_test.go:286: version:362543f6d38f5794a8cb35846a0b75808b2d1bdb,sha256:97cf7179f7eeaad7ed967dd9187bf4f1883c094f0ff9a62540377334fbccb57d +✔✔✔✔✔✔✔✔✔✔✔✔ + + +47 total assertions + +=== RUN TestFile/test_upload_version_commit_error +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + test upload version commit error file_test.go:1217: test case 0 +✔✔ file_test.go:1217: test case 1 +✔✔ file_test.go:1217: test case 2 +✔✔ file_test.go:1217: test case 3 +✔✔ file_test.go:1217: test case 4 +✔✔✔✔✔✔ file_test.go:1253: test upload version commit error end,rpc error: code = Internal desc = head error + + + +61 total assertions + +=== RUN TestFile/test_download +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config + + download success ✔✔✔ + + +64 total assertions + + + download with version1 ✔✔✔ + + +67 total assertions + + + download with version2 ✔✔✔ + + +70 total assertions + + + download with version3 ✔✔✔ + + +73 total assertions + + + download with latest version ✔✔✔ + + +76 total assertions + + + download fail with invalidate key ✔✔ + + +78 total assertions + + + download fail with version and invalidate key ✔✔ + + +80 total assertions + + + test download fail ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + file_test.go:466: filepath:/tmp/upload/tester-20240604021541/test/upload.test2 +✔✔✔✔✔✔✔ + + +89 total assertions + +=== RUN TestFile/test_initiate_upload_file +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test init parts upload file success ✔✔✔✔ + + +93 total assertions + + + test init parts upload file fail ✔✔✔✔ + + +97 total assertions + +=== RUN TestFile/test_upload_parts_file +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test upload parts file success 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + upload 452898186730672128 for 1 part + upload 452898186730672128 for 2 part ✔ + + +98 total assertions + + + upload 452898186730672128 for 3 part ✔ + + +99 total assertions + +✔ + + +100 total assertions + + + upload 452898186730672128 for 4 part + upload 452898186730672128 for 5 part ✔ + + +101 total assertions + +✔ + + +102 total assertions + + + upload 452898186730672128 for 6 part ✔ + + +103 total assertions + +✔ + + +104 total assertions + + + test upload parts file fail ✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +119 total assertions + +=== RUN TestFile/test_complete_parts_file +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test complete parts file success ✔✔✔✔ + + +123 total assertions + + + test complete parts file fail ✔✔✔✔✔✔✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898190501351425 for 1 part ✔ + + +132 total assertions + +✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898190614597632 for 1 part ✔ + + +135 total assertions + +✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898190732038144 for 1 part ✔ + + +138 total assertions + +✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898190845284352 for 1 part ✔ + + +141 total assertions + +✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898190979502080 for 1 part ✔ + + +144 total assertions + +✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898191113719808 for 1 part ✔ + + +147 total assertions + +✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + upload 452898191247937536 for 1 part ✔ + + +150 total assertions + +✔✔ + + +152 total assertions + +=== RUN TestFile/test_file_metadata +2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config + + test file metadata ✔✔✔✔✔✔✔✔ + + +160 total assertions + + + test file metadata fail ✔✔2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔ + + +169 total assertions + +=== RUN TestFile/test_version_reader +2024/06/04 02:15:44 config dir: /data/work/begonia-org/begonia/config + + test version reader ✔✔✔✔✔✔✔ + + +176 total assertions + + + test version reader fail 2024/06/04 02:15:44 config dir: /data/work/begonia-org/begonia/config +FAIL github.com/begonia-org/begonia/internal/biz/file 3.433s +2024/06/04 02:15:41 Start testing +=== RUN TestApp +=== RUN TestApp/add_app + + test app add success app_test.go:44: add test +2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔ + + +9 total assertions + +=== RUN TestApp/get_app + + test app get success app_test.go:109: get test +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔ + + +14 total assertions + +=== RUN TestApp/get_appid + + test get appid 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔ + + +23 total assertions + +=== RUN TestApp/test_cache + + test cache 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔ + + +26 total assertions + +=== RUN TestApp/add_duplicate_name + + test app add duplicate name app_test.go:129: add test +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +28 total assertions + +=== RUN TestApp/patch_app + + test app patch success app_test.go:158: patch test +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔ + + +37 total assertions + +=== RUN TestApp/list_app +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test app list ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +60 total assertions + +=== RUN TestApp/del_app + + test app delete success app_test.go:313: delete test +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔ + + +68 total assertions + +--- PASS: TestApp (1.26s) + --- PASS: TestApp/add_app (1.17s) + --- PASS: TestApp/get_app (0.00s) + --- PASS: TestApp/get_appid (0.00s) + --- PASS: TestApp/test_cache (0.00s) + --- PASS: TestApp/add_duplicate_name (0.00s) + --- PASS: TestApp/patch_app (0.01s) + --- PASS: TestApp/list_app (0.06s) + --- PASS: TestApp/del_app (0.01s) +=== RUN TestAuthzRepo +=== RUN TestAuthzRepo/testCacheToken + + test cache token 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔ + + +69 total assertions + +=== RUN TestAuthzRepo/testGetToken +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + test get token ✔✔ + + +71 total assertions + + + test token expiration ✔ + + +72 total assertions + +=== RUN TestAuthzRepo/deleteToken +2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config + + test delete exp token ✔ + + +73 total assertions + + + test token delete ✔✔{"file":"/data/work/begonia-org/go-layered-cache/kv.go","fn":"Get","level":"ERROR","line":103,"message":"local.Get test:token2 error:Entry not found","timestamp":"2024-06-04 02:15:49"} +✔ + + +76 total assertions + +=== RUN TestAuthzRepo/testPutBlacklist +2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config + + test put blacklist ✔ + + +77 total assertions + +=== RUN TestAuthzRepo/testCheckInBlackList +2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config + + test check in blacklist ✔✔✔✔ + + +81 total assertions + +--- PASS: TestAuthzRepo (7.01s) + --- PASS: TestAuthzRepo/testCacheToken (0.00s) + --- PASS: TestAuthzRepo/testGetToken (7.00s) + --- PASS: TestAuthzRepo/deleteToken (0.00s) + --- PASS: TestAuthzRepo/testPutBlacklist (0.00s) + --- PASS: TestAuthzRepo/testCheckInBlackList (0.00s) +=== RUN TestGetFromLocalErr + + test get from local err 2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔ + + +90 total assertions + +--- PASS: TestGetFromLocalErr (0.00s) +=== RUN TestCacheWatch + + test cache watch 2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:49 config dir: /data/work/begonia-org/begonia/config +✔✔✔ + + +93 total assertions + +--- PASS: TestCacheWatch (2.84s) +=== RUN TestAssertDeletedModel + + test assert deleted model ✔✔✔✔✔✔✔✔ + + +101 total assertions + +--- PASS: TestAssertDeletedModel (0.00s) +=== RUN TestGetPrimaryColumnValueErr + + test get primary column value err ✔✔✔✔ + + +105 total assertions + +--- PASS: TestGetPrimaryColumnValueErr (0.00s) +=== RUN TestEndpoint +=== RUN TestEndpoint/put + + test app add success endpoint_test.go:33: add test +2024/06/04 02:15:52 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔ + + +111 total assertions + +=== RUN TestEndpoint/checkServiceNameExistsTest + + test check service name exists 2024/06/04 02:15:52 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔ + + +116 total assertions + +=== RUN TestEndpoint/get + + test endpoint get success 2024/06/04 02:15:52 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +120 total assertions + +=== RUN TestEndpoint/getKeysByTags + + test get keys by tags success 2024/06/04 02:15:52 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔ + + +125 total assertions + +=== RUN TestEndpoint/list +2024/06/04 02:15:52 config dir: /data/work/begonia-org/begonia/config +FAIL github.com/begonia-org/begonia/internal/data 11.427s +=== RUN TestStreamInterceptor + + test stream interceptor ✔✔ + + +2 total assertions + +--- PASS: TestStreamInterceptor (0.03s) +=== RUN TestUnaryInterceptor + + test unary interceptor ✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +32 total assertions + +--- PASS: TestUnaryInterceptor (0.05s) +=== RUN TestMiddlewareUnaryInterceptorChains + + test middleware unary interceptor chains 2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin exception priority 0","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin http priority 2","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin params_validator priority 3","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin auth priority 4","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin logger priority 1","timestamp":"2024-06-04 02:15:42"} +✔✔{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin logger priority 1","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin exception priority 0","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin http priority 2","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin params_validator priority 3","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin auth priority 4","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin test priority 1","timestamp":"2024-06-04 02:15:42"} +✔{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin http priority 2","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin params_validator priority 3","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin auth priority 4","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin logger priority 1","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin exception priority 0","timestamp":"2024-06-04 02:15:42"} +✔ + + +36 total assertions + +--- PASS: TestMiddlewareUnaryInterceptorChains (1.24s) +=== RUN TestPluginUnaryInterceptor + + test plugin unary interceptor ✔✔✔✔✔✔✔ + + +43 total assertions + +--- PASS: TestPluginUnaryInterceptor (2.00s) +=== RUN TestPluginUnaryInterceptorErr + + test plugin unary interceptor failed to listen: listen tcp :9850: bind: address already in use✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +59 total assertions + +--- PASS: TestPluginUnaryInterceptorErr (2.00s) +=== RUN TestPluginStreamInterceptorErr + + test plugin unary interceptor failed to listen: listen tcp :9850: bind: address already in use✔✔✔✔✔✔✔✔ + + +67 total assertions + +--- PASS: TestPluginStreamInterceptorErr (2.03s) +=== RUN TestValidatorUnaryInterceptor + + test validator unary interceptor ✔✔✔✔✔ vaildator_test.go:59: rpc error: code = InvalidArgument desc = params SubName validation failed: +✔✔ + + +74 total assertions + +--- PASS: TestValidatorUnaryInterceptor (0.00s) +=== RUN TestValidatorStreamInterceptor + + test stream interceptor ✔ + + +75 total assertions + +--- PASS: TestValidatorStreamInterceptor (0.00s) +PASS + github.com/begonia-org/begonia/internal/middleware coverage: 99.3% of statements +ok github.com/begonia-org/begonia/internal/middleware 7.459s coverage: 99.3% of statements +=== RUN TestAccessKeyAuthMiddleware + + TestAccessKeyAuthMiddleware 2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔{"file":"/data/work/begonia-org/begonia/internal/middleware/auth/aksk.go","fn":"func1","level":"ERROR","line":108,"message":"StreamResponseAfter error,StreamResponseAfter err","timestamp":"2024-06-04 02:15:42"} +✔✔ + + +6 total assertions + +--- PASS: TestAccessKeyAuthMiddleware (1.20s) +=== RUN TestRequestBeforeErr + + TestRequestBeforeErr 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔ + + +13 total assertions + +--- PASS: TestRequestBeforeErr (0.00s) +=== RUN TestValidateStream + + TestValidateStream 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔ + + +16 total assertions + +--- PASS: TestValidateStream (0.00s) +=== RUN TestAPIKeyUnaryInterceptor + + TestAPIKeyUnaryInterceptor 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔ + + +23 total assertions + +--- PASS: TestAPIKeyUnaryInterceptor (0.02s) +=== RUN TestApiKeyStreamInterceptor + + TestApiKeyStreamInterceptor 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +27 total assertions + +--- PASS: TestApiKeyStreamInterceptor (0.02s) +=== RUN TestUnaryInterceptor +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + TestUnaryInterceptor api-key ✔✔✔✔ + + +31 total assertions + + + TestUnaryInterceptor jwt 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +{"file":"/data/work/begonia-org/go-layered-cache/cuckoo.go","fn":"Check","level":"ERROR","line":100,"message":"local.Get test:begonia:filter:user:black error:not found such bloom filter test:begonia:filter:user:black","timestamp":"2024-06-04 02:15:42"} +{"file":"/data/work/begonia-org/begonia/internal/data/cache.go","fn":"CheckInFilter","level":"ERROR","line":98,"message":"check value of test:begonia:filter:user:black error:not found such bloom filter test:begonia:filter:user:black","timestamp":"2024-06-04 02:15:42"} +✔✔ + + +33 total assertions + + + TestUnaryInterceptor aksk ✔✔✔✔✔✔✔ + + +40 total assertions + +--- PASS: TestUnaryInterceptor (0.03s) +=== RUN TestStreamInterceptor +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + TestStreamInterceptor apikey ✔✔ + + +42 total assertions + + + TestStreamInterceptor jwt 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +44 total assertions + + + TestUnaryInterceptor aksk ✔✔✔✔✔✔✔ + + +51 total assertions + +--- PASS: TestStreamInterceptor (0.03s) +=== RUN TestTestUnaryInterceptorErr +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + TestUnaryInterceptor err ✔✔✔ + + +54 total assertions + +--- PASS: TestTestUnaryInterceptorErr (0.02s) +=== RUN TestStreamInterceptorErr +2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config + + TestStreamInterceptor err ✔✔✔✔✔ + + +59 total assertions + +--- PASS: TestStreamInterceptorErr (0.03s) +=== RUN TestHeaders + + TestHeaders ✔ + + +60 total assertions + +--- PASS: TestHeaders (0.00s) +=== RUN TestJWTUnaryInterceptor + + TestJWTUnaryInterceptor 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔{"file":"/data/work/begonia-org/begonia/internal/middleware/auth/jwt.go","fn":"func1","level":"ERROR","line":130,"message":"refresh token fail,test","timestamp":"2024-06-04 02:15:56","x-access-key":"","x-http-forwarded-for":"","x-http-method":"","x-http-uri":"","x-request-id":"","x-uid":""} +✔✔ + + +89 total assertions + +--- PASS: TestJWTUnaryInterceptor (13.44s) +=== RUN TestJWTStreamInterceptor + + TestJWTStreamInterceptor 2024/06/04 02:15:56 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +93 total assertions + +--- PASS: TestJWTStreamInterceptor (0.02s) +PASS + github.com/begonia-org/begonia/internal/middleware/auth coverage: 98.7% of statements +ok github.com/begonia-org/begonia/internal/middleware/auth 14.915s coverage: 98.7% of statements +=== RUN TestConstant + constant_test.go:11: TestConstant + + TestConstant ✔ + + +1 total assertion + +--- PASS: TestConstant (0.00s) +PASS + github.com/begonia-org/begonia/internal/pkg coverage: [no statements] +ok github.com/begonia-org/begonia/internal/pkg 0.029s coverage: [no statements] +=== RUN TestConfig + + TestConfig 2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔2024/06/04 02:15:41 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +65 total assertions + +--- PASS: TestConfig (0.01s) +PASS + github.com/begonia-org/begonia/internal/pkg/config coverage: 97.2% of statements +ok github.com/begonia-org/begonia/internal/pkg/config 0.065s coverage: 97.2% of statements +=== RUN TestGenerateRandPasswd + + TestGenerateRandPasswd 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔ + + +5 total assertions + +--- PASS: TestGenerateRandPasswd (0.00s) +=== RUN TestNewUsersAuth + + TestNewUsersAuth 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +9 total assertions + +--- PASS: TestNewUsersAuth (0.00s) +=== RUN TestGenerateRandSeed + + TestGenerateRandSeed 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔ + + +10 total assertions + +--- PASS: TestGenerateRandSeed (0.00s) +=== RUN TestGenerateKeys + + TestGenerateKeys 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔ + + +16 total assertions + +--- PASS: TestGenerateKeys (0.02s) +=== RUN TestEncryptWithPublicKey + + TestEncrypt With PublicKey 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔ + + +25 total assertions + +--- PASS: TestEncryptWithPublicKey (0.04s) +=== RUN TestEncryptAES + + TestEncryptAES 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔ + + +31 total assertions + +--- PASS: TestEncryptAES (0.00s) +=== RUN TestGenerateAuthSeed + + TestGenerateAuthSeed 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔ + + +39 total assertions + +--- PASS: TestGenerateAuthSeed (0.00s) +=== RUN TestRSADecrypt + + TestRSADecrypt 2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔ + + +46 total assertions + +--- PASS: TestRSADecrypt (0.01s) +PASS + github.com/begonia-org/begonia/internal/pkg/crypto coverage: 100.0% of statements +ok github.com/begonia-org/begonia/internal/pkg/crypto 0.148s coverage: 100.0% of statements +=== RUN TestLoadAllRouters + + TestLoadAllRouters ✔✔✔✔✔✔✔✔ + + +8 total assertions + +--- PASS: TestLoadAllRouters (0.03s) +=== RUN TestDeleteRouters + + TestDeleteRouters ✔✔ + + +10 total assertions + +--- PASS: TestDeleteRouters (0.03s) +PASS + github.com/begonia-org/begonia/internal/pkg/routers coverage: 97.0% of statements +ok github.com/begonia-org/begonia/internal/pkg/routers 0.090s coverage: 97.0% of statements +=== RUN TestGenerateRandomString + + TestGenerateRandomString ✔✔✔✔ + + +4 total assertions + +--- PASS: TestGenerateRandomString (0.00s) +=== RUN TestLoadPublicKeyFromFile + + TestLoadPublicKeyFromFile ✔✔2024/06/04 02:15:42 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔ + + +14 total assertions + +--- PASS: TestLoadPublicKeyFromFile (0.00s) +PASS + github.com/begonia-org/begonia/internal/pkg/utils coverage: 100.0% of statements +ok github.com/begonia-org/begonia/internal/pkg/utils 0.022s coverage: 100.0% of statements +2024/06/04 02:15:43 run test server +2024/06/04 02:15:43 config dir: /data/work/begonia-org/begonia/config +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin logger priority 1","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin exception priority 0","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin http priority 2","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin params_validator priority 3","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/middleware/middleware.go","fn":"New","level":"INFO","line":49,"message":"plugin auth priority 4","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"Do","level":"INFO","line":62,"message":"start watch","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:44"} +2024/06/04 02:15:44 lockKey:test:begonia:user:black:lock +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":81,"message":"start action","timestamp":"2024-06-04 02:15:44"} +2024/06/04 02:15:44 lastUpdate:2024-06-04 02:15:42.904 +0800 CST +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"loadUsersBlacklist","level":"INFO","line":134,"message":"load users:1","timestamp":"2024-06-04 02:15:44"} +{"file":"/data/work/begonia-org/begonia/internal/biz/data.go","fn":"func1","level":"INFO","line":123,"message":"unlock success","timestamp":"2024-06-04 02:15:44"} +2024/06/04 02:15:47 Start on 0.0.0.0:12140 +=== RUN TestApp +=== RUN TestApp/add_app + + test add app {"elapsed":"18.215954ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Post","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/apps","x-request-id":"2a8266ba-aaa8-4026-9929-b585c85af860","x-uid":""} +✔✔✔{"elapsed":"607.458µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Get","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"101c4cc6-0c72-47d9-b379-20498355bd4a","x-uid":""} +✔✔✔{"elapsed":"2.650465ms","file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Put.func1","level":"ERROR","line":105,"message":"rpc error: code = AlreadyExists desc = add app failed: Error 1062 (23000): Duplicate entry 'app-20240604021550' for key 'apps.uni_apps_name'","module":"request","name":"/begonia.org.sdk.AppsService/Post","status":5102,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/apps","x-request-id":"0b024ba9-7489-4cb1-9dc0-f17e55bd3e4d","x-uid":""} +{"file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Put.func1","level":"ERROR","line":105,"message":"add app failed: Error 1062 (23000): Duplicate entry 'app-20240604021550' for key 'apps.uni_apps_name'","status":409,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"POST","x-http-uri":"/api/v1/apps","x-request-id":"b221f85e-0111-454d-bceb-4f93d6b0a1bd","x-uid":""} +✔✔{"elapsed":"5.081279ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Post","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/apps","x-request-id":"921caa30-2a54-4635-8467-4d21685d941c","x-uid":""} +✔✔ + + +10 total assertions + +=== RUN TestApp/get_app + + test get app {"elapsed":"560.001µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Get","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"2b5497a1-772c-43ac-b0af-0014f9cbe508","x-uid":""} +✔✔ + + +12 total assertions + +=== RUN TestApp/list_app + + test list app {"elapsed":"4.020027ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/List","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/apps?page=1\u0026page_size=10\u0026status=APP_ENABLED\u0026tags=test\u0026tags=test2","x-request-id":"f3e668a9-ef3e-4948-92ca-865d791eb0f7","x-uid":""} +✔✔✔✔ + + +16 total assertions + +=== RUN TestApp/list_app_err + + test list app 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +18 total assertions + +=== RUN TestApp/patch_app + + test patch app {"elapsed":"5.094546ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Update","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"PUT","x-http-uri":"/api/v1/apps","x-request-id":"cff9c8e8-a6e3-4c15-84c9-0e9fb109e9ca","x-uid":""} +✔✔{"elapsed":"483.604µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Get","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"e70294a2-f9c8-4e5f-8c94-63b4617d486e","x-uid":""} +✔✔✔{"elapsed":"3.001971ms","file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Patch","level":"ERROR","line":166,"message":"rpc error: code = Internal desc = update model for appid=452898220301881344 failed: Error 1062 (23000): Duplicate entry 'app-service-2-20240604021550' for key 'apps.uni_apps_name'","module":"request","name":"/begonia.org.sdk.AppsService/Update","status":5102,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"PUT","x-http-uri":"/api/v1/apps","x-request-id":"bc9ff890-eee8-4950-922e-31593bc94d53","x-uid":""} +{"file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Patch","level":"ERROR","line":166,"message":"update model for appid=452898220301881344 failed: Error 1062 (23000): Duplicate entry 'app-service-2-20240604021550' for key 'apps.uni_apps_name'","status":500,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"PUT","x-http-uri":"/api/v1/apps","x-request-id":"05d8846c-1e95-4a96-930e-d86715d25045","x-uid":""} +✔✔ + + +25 total assertions + +=== RUN TestApp/del_app + + test del app {"elapsed":"5.470225ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AppsService/Delete","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"13a42d6f-1e4c-4d66-817f-d9186c273ee7","x-uid":""} +✔✔{"file":"/data/work/begonia-org/begonia/internal/service/app.go","fn":"Get","level":"ERROR","line":34,"message":"GetApps failed: rpc error: code = NotFound desc = get app failed: get model failed: record not found","timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"49aa4aac-8d2f-4789-8582-a797e6c9a6fa","x-uid":""} +{"elapsed":"641.822µs","file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Get","level":"ERROR","line":131,"message":"rpc error: code = NotFound desc = get app failed: get model failed: record not found","module":"request","name":"/begonia.org.sdk.AppsService/Get","status":5108,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"49aa4aac-8d2f-4789-8582-a797e6c9a6fa","x-uid":""} +{"file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Get","level":"ERROR","line":131,"message":"get app failed: get model failed: record not found","status":404,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"GET","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"323a21b4-8b6f-4b4e-ada1-78492d358c28","x-uid":""} +✔{"elapsed":"432.914µs","file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Del","level":"ERROR","line":145,"message":"rpc error: code = NotFound desc = get app failed: get model failed: record not found","module":"request","name":"/begonia.org.sdk.AppsService/Delete","status":5108,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"23831134-0c21-4328-b8aa-28239a82847c","x-uid":""} +{"file":"/data/work/begonia-org/begonia/internal/biz/app.go","fn":"github.com/begonia-org/begonia/internal/biz.(*AppUsecase).Del","level":"ERROR","line":145,"message":"get app failed: get model failed: record not found","status":404,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"DELETE","x-http-uri":"/api/v1/apps/452898220301881344","x-request-id":"4af9eddf-c94c-492f-9c29-73ac87b30efb","x-uid":""} +✔✔ + + +30 total assertions + +--- PASS: TestApp (0.07s) + --- PASS: TestApp/add_app (0.03s) + --- PASS: TestApp/get_app (0.00s) + --- PASS: TestApp/list_app (0.01s) + --- PASS: TestApp/list_app_err (0.00s) + --- PASS: TestApp/patch_app (0.01s) + --- PASS: TestApp/del_app (0.01s) +=== RUN TestAuth +=== RUN TestAuth/login + + test login ✔{"elapsed":"72.428µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AuthService/AuthSeed","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/auth/log/1717438550227000","x-request-id":"2d103137-9824-4e11-a25b-fdec472583a8","x-uid":""} +{"elapsed":"2.339ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AuthService/Login","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/auth/login","x-request-id":"5bc9be20-60a3-450c-b0bb-50745c0c3fe5","x-uid":""} +✔✔✔✔{"file":"/data/work/begonia-org/go-layered-cache/cuckoo.go","fn":"Check","level":"ERROR","line":100,"message":"local.Get test:begonia:filter:user:black error:not found such bloom filter test:begonia:filter:user:black","timestamp":"2024-06-04 02:15:50"} +{"file":"/data/work/begonia-org/begonia/internal/data/cache.go","fn":"CheckInFilter","level":"ERROR","line":98,"message":"check value of test:begonia:filter:user:black error:not found such bloom filter test:begonia:filter:user:black","timestamp":"2024-06-04 02:15:50"} +{"elapsed":"994ns","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.SystemService/Get","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/v1/gateway/info","x-request-id":"1f0327a1-c1f6-4150-904b-c44c209d4630","x-uid":""} +✔✔✔✔✔✔ authz_test.go:92: test-20240507141004 20240604021539 e5fe9d5 + + + +41 total assertions + +=== RUN TestAuth/logout + + test logout {"file":"/data/work/begonia-org/go-layered-cache/cuckoo.go","fn":"Add","level":"ERROR","line":111,"message":"source.Set:context canceled","timestamp":"2024-06-04 02:15:50"} +{"elapsed":"4.748µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"ERROR","line":161,"message":"logout error","module":"request","name":"/begonia.org.sdk.AuthService/Logout","status":500,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/auth/logout","x-request-id":"d7075c07-cbce-4c3c-9ed3-49003816516e","x-uid":""} +{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"logout error","status":500,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"DELETE","x-http-uri":"/api/v1/auth/logout","x-request-id":"e4149e49-6aff-4b9a-9d7c-fd277baa11e9","x-uid":""} +✔{"elapsed":"106.297µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.AuthService/Logout","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/auth/logout","x-request-id":"b404ce71-3a74-4f36-9bd7-523d46ea8882","x-uid":""} +✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"check token error,rpc error: code = Unauthenticated desc = token在黑名单中 or %!!(MISSING)w(\u003cnil\u003e)","status":401,"timestamp":"2024-06-04 02:15:50","x-access-key":"","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"GET","x-http-uri":"/v1/gateway/info","x-request-id":"f6165060-30fa-4e3f-8fe9-db76c795dda0","x-uid":""} +✔✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"func1","level":"ERROR","line":279,"message":"check token error,rpc error: code = Unauthenticated desc = token在黑名单中 or %!!(MISSING)w(\u003cnil\u003e)","status":401,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"DELETE","x-http-uri":"/api/v1/auth/logout","x-request-id":"a11d16a8-2d57-4e7b-81c8-499113e57b37","x-uid":""} +✔ authz_test.go:130: logout error: Unknown error + + + +50 total assertions + +=== RUN TestAuth/auth_seed + + test auth seed 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔ + + +51 total assertions + +--- PASS: TestAuth (0.01s) + --- PASS: TestAuth/login (0.01s) + --- PASS: TestAuth/logout (0.00s) + --- PASS: TestAuth/auth_seed (0.00s) +=== RUN TestFile +=== RUN TestFile/upload + + test upload file {"elapsed":"9.173166ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/Upload","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/files","x-request-id":"6b27c120-b615-4eec-8493-7cba5d580f10","x-uid":""} +✔✔✔2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +58 total assertions + +=== RUN TestFile/download + + test download file ✔{"elapsed":"101.307µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/Download","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/files?key=449250203195674624%2Ftest%2Fhelloworld.pb","x-request-id":"b75d399d-a6de-4234-8db2-a1b93db212cb","x-uid":""} +✔✔✔ file_test.go:188: 8b8812a5661179bef9ef057ec3c448cf244f9ddf6bd669bfd681e5696ae616be +✔ + + +63 total assertions + +=== RUN TestFile/testUploadErr + + test upload file err 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +67 total assertions + +=== RUN TestFile/testDownloadErr + + test download file err 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔ + + +73 total assertions + +=== RUN TestFile/uploadParts + + test upload file ✔{"elapsed":"84.459µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/InitiateMultipartUpload","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/files/part/init","x-request-id":"13a68ff6-eafa-43b0-b3e7-43885fe00399","x-uid":""} +{"elapsed":"8.583913ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/UploadMultipartFile","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"PUT","x-http-uri":"/api/v1/files/part","x-request-id":"49ba1ed2-7b6b-4346-8b46-cfdbf2e8a226","x-uid":""} +{"elapsed":"77.537552ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/CompleteMultipartUpload","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/files/part/complete","x-request-id":"9a9f3e77-1571-4d6e-a5c3-83ce4442ed7b","x-uid":""} +✔✔✔✔2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +80 total assertions + +=== RUN TestFile/testRangeDownload + + test range download file ✔{"elapsed":"39.56µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/DownloadForRange","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/files/part?key=449250203195674624%2Ftest%2Ftmp.bin","x-request-id":"7664bf2e-671b-46f0-9048-b03b83148450","x-uid":""} +✔✔{"elapsed":"1.15571ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/DownloadForRange","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/files/part?key=449250203195674624%2Ftest%2Ftmp.bin","x-request-id":"15b694e6-f56a-4196-8721-fe99a56dcd20","x-uid":""} +✔✔ + + +85 total assertions + +=== RUN TestFile/testAbortUpload + + test range download file {"elapsed":"26.185µs","file":"/data/work/begonia-org/begonia/internal/biz/file/file.go","fn":"github.com/begonia-org/begonia/internal/biz/file.(*FileUsecase).AbortMultipartUpload","level":"ERROR","line":356,"message":"rpc error: code = NotFound desc = uploadId未找到","module":"request","name":"/begonia.org.sdk.FileService/AbortMultipartUpload","status":6105,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/files/part/abort","x-request-id":"0408a40c-7fca-4386-9bc6-3514ea06b2be","x-uid":""} +{"file":"/data/work/begonia-org/begonia/internal/biz/file/file.go","fn":"github.com/begonia-org/begonia/internal/biz/file.(*FileUsecase).AbortMultipartUpload","level":"ERROR","line":356,"message":"uploadId未找到","status":404,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36978","x-http-method":"POST","x-http-uri":"/api/v1/files/part/abort","x-request-id":"d178acb6-40e5-49b8-8d70-0ada02a56f87","x-uid":""} +✔ + + +86 total assertions + +=== RUN TestFile/testRangeDownloadErr + + test range download file err 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔✔ + + +102 total assertions + +=== RUN TestFile/downloadParts + + test download parts file ✔{"elapsed":"23.464014ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/Metadata","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"HEAD","x-http-uri":"/api/v1/files/metadata?key=449250203195674624%2Ftest%2Ftmp.bin","x-request-id":"5b6644d5-fb0f-4210-815a-d0010fe4a866","x-uid":""} +{"elapsed":"761.613µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/DownloadForRange","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/files/part?key=449250203195674624%2Ftest%2Ftmp.bin","x-request-id":"3b8455cb-4ba0-4974-af52-9048a0f9998f","x-uid":""} +✔✔ + + +105 total assertions + +=== RUN TestFile/deleteFile + + test delete file {"elapsed":"431.63µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.FileService/Delete","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/files?key=449250203195674624%2Ftest%2Fhelloworld.pb","x-request-id":"a86316bf-a48b-45a0-bf50-a77087a8f6fe","x-uid":""} +✔✔2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔ + + +108 total assertions + +=== RUN TestFile/testDelErr + + test delete file err 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +110 total assertions + +=== RUN TestFile/testMetaErr + + test meta file err 2024/06/04 02:15:50 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔ + + +114 total assertions + +--- PASS: TestFile (0.32s) + --- PASS: TestFile/upload (0.01s) + --- PASS: TestFile/download (0.00s) + --- PASS: TestFile/testUploadErr (0.00s) + --- PASS: TestFile/testDownloadErr (0.00s) + --- PASS: TestFile/uploadParts (0.24s) + --- PASS: TestFile/testRangeDownload (0.01s) + --- PASS: TestFile/testAbortUpload (0.00s) + --- PASS: TestFile/testRangeDownloadErr (0.00s) + --- PASS: TestFile/downloadParts (0.05s) + --- PASS: TestFile/deleteFile (0.00s) + --- PASS: TestFile/testDelErr (0.00s) + --- PASS: TestFile/testMetaErr (0.00s) +=== RUN TestEndpoint +=== RUN TestEndpoint/post + + test create endpoint api ✔{"elapsed":"16.567491ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.EndpointService/Post","status":200,"timestamp":"2024-06-04 02:15:50","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/endpoints","x-request-id":"75434e91-87e4-45ef-8f3f-95249524599f","x-uid":""} +✔✔✔✔{"elapsed":"1.951039ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/helloworld.Greeter/SayHelloGet","status":200,"timestamp":"2024-06-04 02:15:51","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/helloworld","x-request-id":"f63a0520-d59c-45e3-8c8b-c71b363b0fb2","x-uid":""} +✔✔ + + +121 total assertions + +=== RUN TestEndpoint/patch + + test patch endpoint api {"elapsed":"14.741349ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.EndpointService/Update","status":200,"timestamp":"2024-06-04 02:15:51","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"PUT","x-http-uri":"/api/v1/endpoints/452898221971214336","x-request-id":"38cf3924-c8ef-4f39-a3f0-3641ca7273eb","x-uid":""} +✔✔ + + +123 total assertions + +=== RUN TestEndpoint/get + + test get endpoint api {"elapsed":"581.154µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.EndpointService/Get","status":200,"timestamp":"2024-06-04 02:15:54","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/endpoints/452898221971214336","x-request-id":"feb1929a-a241-4a6e-9a1a-2fb54ecd486a","x-uid":""} +✔✔✔ + + +126 total assertions + +=== RUN TestEndpoint/list + + test list endpoint api {"elapsed":"5.844533ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.EndpointService/List","status":200,"timestamp":"2024-06-04 02:15:54","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/endpoints?tags=test\u0026tags=test2","x-request-id":"ca1e3a41-3a85-4c6b-a1e6-00019a47a777","x-uid":""} +✔✔✔ + + +129 total assertions + +=== RUN TestEndpoint/testErr + + test endpoint server error 2024/06/04 02:15:54 config dir: /data/work/begonia-org/begonia/config +✔✔✔✔✔✔ + + +135 total assertions + +=== RUN TestEndpoint/del + + test delete endpoint api {"elapsed":"16.479626ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.EndpointService/Delete","status":200,"timestamp":"2024-06-04 02:15:54","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/endpoints/452898221971214336","x-request-id":"9ba336b5-f04c-4d92-86ee-d108abaf6608","x-uid":""} +✔✔✔{"file":"/data/work/begonia-org/begonia/gateway/http.go","fn":"func1","level":"WARNING","line":363,"message":"not found router","timestamp":"2024-06-04 02:15:59","x-access-key":"","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/example/helloworld","x-request-id":"ff4cf145-09af-4989-ad34-331fdd2d4bc3","x-uid":""} +✔✔ + + +140 total assertions + +--- PASS: TestEndpoint (9.09s) + --- PASS: TestEndpoint/post (1.02s) + --- PASS: TestEndpoint/patch (3.02s) + --- PASS: TestEndpoint/get (0.00s) + --- PASS: TestEndpoint/list (0.02s) + --- PASS: TestEndpoint/testErr (0.00s) + --- PASS: TestEndpoint/del (5.02s) +=== RUN TestUser +=== RUN TestUser/add_user + + test add user {"elapsed":"14.530642ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.UserService/Register","status":200,"timestamp":"2024-06-04 02:15:59","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"POST","x-http-uri":"/api/v1/users","x-request-id":"1c256560-de82-4608-b55a-592bb2f1fa94","x-uid":""} +✔✔✔ + + +143 total assertions + +=== RUN TestUser/get_user + + test get user {"elapsed":"457.003µs","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.UserService/Get","status":200,"timestamp":"2024-06-04 02:15:59","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/users/452898260093243392","x-request-id":"6a424e71-5f3b-4f21-8baa-9f0a89c7971e","x-uid":""} +✔✔ + + +145 total assertions + +=== RUN TestUser/patch_user + + test patch user {"elapsed":"5.001363ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.UserService/Update","status":200,"timestamp":"2024-06-04 02:15:59","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"PUT","x-http-uri":"/api/v1/users/452898260093243392","x-request-id":"fef2ea43-1d7b-4a54-a9a3-e9c63c7cbe89","x-uid":""} +✔✔ + + +147 total assertions + +=== RUN TestUser/delete_user + + test delete user {"elapsed":"4.923405ms","file":"/data/work/begonia-org/begonia/gateway/middlewares.go","fn":"logger","level":"INFO","line":163,"message":"success","module":"request","name":"/begonia.org.sdk.UserService/Delete","status":200,"timestamp":"2024-06-04 02:15:59","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"DELETE","x-http-uri":"/api/v1/users/452898260093243392","x-request-id":"1c52ebce-95bf-4184-a516-25407c9079ad","x-uid":""} +✔✔{"elapsed":"367.879µs","file":"/data/work/begonia-org/begonia/internal/biz/user.go","fn":"github.com/begonia-org/begonia/internal/biz.(*UserUsecase).Get","level":"ERROR","line":58,"message":"rpc error: code = NotFound desc = get user failed: get model failed: record not found","module":"request","name":"/begonia.org.sdk.UserService/Get","status":4113,"timestamp":"2024-06-04 02:15:59","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1","x-http-method":"GET","x-http-uri":"/api/v1/users/452898260093243392","x-request-id":"96c099f0-c3b1-4ea3-8ff4-dede488f9f89","x-uid":""} +{"file":"/data/work/begonia-org/begonia/internal/biz/user.go","fn":"github.com/begonia-org/begonia/internal/biz.(*UserUsecase).Get","level":"ERROR","line":58,"message":"get user failed: get model failed: record not found","status":404,"timestamp":"2024-06-04 02:15:59","x-access-key":"P6DKyCxyz2ewz4brsFPJtusyMLYh4uex","x-http-forwarded-for":"127.0.0.1:36986","x-http-method":"GET","x-http-uri":"/api/v1/users/452898260093243392","x-request-id":"bad74af4-7264-4b8f-88d3-c6bff84cc584","x-uid":""} +✔ + + +150 total assertions + +=== RUN TestUser/test_register_user_error + + test register user error 2024/06/04 02:15:59 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +152 total assertions + +=== RUN TestUser/test_update_user_error + + test update user error 2024/06/04 02:15:59 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +154 total assertions + +=== RUN TestUser/test_delete_user_error + + test delete user error 2024/06/04 02:15:59 config dir: /data/work/begonia-org/begonia/config +✔✔ + + +156 total assertions + +--- PASS: TestUser (0.03s) + --- PASS: TestUser/add_user (0.02s) + --- PASS: TestUser/get_user (0.00s) + --- PASS: TestUser/patch_user (0.01s) + --- PASS: TestUser/delete_user (0.01s) + --- PASS: TestUser/test_register_user_error (0.00s) + --- PASS: TestUser/test_update_user_error (0.00s) + --- PASS: TestUser/test_delete_user_error (0.00s) +PASS + github.com/begonia-org/begonia/internal/service coverage: 97.5% of statements +2024/06/04 02:15:59 All tests passed +ok github.com/begonia-org/begonia/internal/service 26.704s coverage: 97.5% of statements +FAIL From 8abeb971fa6dbf962e7e640ac8a98e76e8478e21 Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Sat, 15 Jun 2024 18:07:56 +0800 Subject: [PATCH 04/16] feat:add minio api --- .env | 18 + .github/workflows/go.yml | 40 ++- codecov.yml | 4 + config/settings.yml | 17 +- docker-compose.yml | 24 +- go.mod | 44 +-- go.sum | 98 +++--- internal/biz/endpoint/endpoint.go | 6 +- internal/biz/endpoint/endpoint_test.go | 2 +- internal/biz/file/file.go | 230 +++++++++---- internal/biz/file/file_test.go | 207 ++++++++---- internal/biz/file/minio.go | 208 ++++++++++++ internal/biz/file/minio_test.go | 446 +++++++++++++++++++++++++ internal/data/app_test.go | 2 +- internal/data/data.go | 1 + internal/data/file.go | 96 ++++-- internal/data/file_test.go | 134 ++++++++ internal/data/wire.go | 5 + internal/data/wire_gen.go | 12 + internal/migrate/app.go | 43 ++- internal/migrate/migrate.go | 6 +- internal/migrate/operator_test.go | 149 +++++++++ internal/pkg/config/config.go | 13 + internal/pkg/config/config_test.go | 13 + internal/pkg/constant.go | 18 +- internal/server/coverprofile.cov | 32 +- internal/server/server.go | 9 +- internal/server/server_test.go | 62 ++++ internal/service/base_test.go | 14 +- internal/service/file.go | 100 +++++- internal/service/file_test.go | 119 +++++-- internal/service/wire.go | 2 +- internal/service/wire_gen.go | 13 +- internal/wire.go | 5 +- internal/wire_gen.go | 62 +++- internal/wire_gen_test.go | 52 +++ 36 files changed, 1935 insertions(+), 371 deletions(-) create mode 100644 .env create mode 100644 codecov.yml create mode 100644 internal/biz/file/minio.go create mode 100644 internal/biz/file/minio_test.go create mode 100644 internal/data/file_test.go create mode 100644 internal/migrate/operator_test.go create mode 100644 internal/server/server_test.go create mode 100644 internal/wire_gen_test.go diff --git a/.env b/.env new file mode 100644 index 0000000..c95ed52 --- /dev/null +++ b/.env @@ -0,0 +1,18 @@ +# Increase or decrease based on the available host memory (in bytes) + +MEM_LIMIT=8073741824 + + +MYSQL_PASSWORD=qwe123 +MYSQL_PORT=3306 + +# Port to expose minio to the host +MINIO_CONSOLE_PORT=9001 +MINIO_PORT=9000 + +MINIO_USER=begonia +MINIO_PASSWORD=begoniabegonia + + +TIMEZONE='Asia/Shanghai' + diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4acd253..f3053eb 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -3,10 +3,14 @@ name: Go on: push: paths: - - '**.go' + - "**.go" + - "**.yml" + - "**.mod" pull_request: paths: - - '**.go' + - "**.go" + - "**.yml" + - "**.mod" jobs: test: @@ -17,16 +21,44 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: 1.20.7 + go-version: 1.22.4 - name: Start Docker Composer run: docker-compose up -d - name: Init MySQL run: | + go mod tidy go run ./cmd/begonia init -e dev cat ~/.begonia/admin-app.json - + - name: Init Minio + run: | + go install github.com/minio/mc@latest + cat << EOF > /tmp/policy.json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:*" + ], + "Resource": [ + "arn:aws:s3:::*" + ] + } + ] + } + EOF + - name: Configure Minio + run: | + mc alias set myminio http://127.0.0.1:9000 begonia begoniabegonia + mc admin user add myminio begonia-2 begoniabegonia + mc admin user svcacct add \ + --access-key "begonia" \ + --secret-key "begoniabegonia" \ + --policy "/tmp/policy.json" \ + myminio begonia-2 - name: Type a temporary tag run: git tag test-$(date '+%Y%m%d%H%M%S') diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..d5e44ba --- /dev/null +++ b/codecov.yml @@ -0,0 +1,4 @@ +ignore: + - "cmd" + - "version.go" + - "example" \ No newline at end of file diff --git a/config/settings.yml b/config/settings.yml index 6dea531..0288fe7 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -8,6 +8,13 @@ endpoints: file: upload: dir: /data/work/begonia-org/begonia/upload + engines: + - name: "FILE_ENGINE_MINIO" + endpoint: "127.0.0.1:9000" + accessKey: "7OdVJK1alV8cpRMeBLBW" + secretKey: "2GNNRqqElReC1KnV3kX9jSjyLU4kwOaTZEqDS2vH" + - name: "FILE_ENGINE_LOCAL" + endpoint: "/data/work/begonia-org/begonia/upload" protos: dir: /data/work/begonia-org/begonia-go-sdk/protos desc: /data/work/begonia-org/begonia-go-sdk/protos/api.bin @@ -16,7 +23,8 @@ mysql: port: 3306 user: "test" password: "test" - database: "test" + database: "begonia-test" + table_prefix: "begonia_" admin: name: "admin" password: "admin" @@ -95,6 +103,13 @@ test: protos: dir: "/tmp/protos" desc: "/tmp/protos/api.bin" + engines: + - name: "FILE_ENGINE_MINIO" + endpoint: "127.0.0.1:9000" + accessKey: "begonia" + secretKey: "begoniabegonia" + - name: "FILE_ENGINE_LOCAL" + endpoint: "/tmp/upload" etcd: endpoint: prefix: "/begonia/endpoints" diff --git a/docker-compose.yml b/docker-compose.yml index 5c12d95..214e367 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,17 +7,11 @@ services: restart: always environment: MYSQL_ROOT_PASSWORD: qwe123 - MYSQL_DATABASE: test + MYSQL_DATABASE: "begonia-test" MYSQL_USER: test MYSQL_PASSWORD: test ports: - 3306:3306 - # healthcheck: - # test: ["executable", "arg"] - # interval: 1m30s - # timeout: 30s - # retries: 5 - # start_period: 30s etcd: image: 'bitnami/etcd:latest' environment: @@ -34,4 +28,18 @@ services: container_name: redis restart: always ports: - - 6379:6379 \ No newline at end of file + - 6379:6379 + minio: + image: quay.io/minio/minio:RELEASE.2023-12-20T01-00-02Z + container_name: minio + command: server --console-address ":9001" /data + ports: + - ${MINIO_PORT}:9000 + - 9001:9001 + env_file: + - ./.env + environment: + - MINIO_ROOT_USER=${MINIO_USER} + - MINIO_ROOT_PASSWORD=${MINIO_PASSWORD} + - TZ=${TIMEZONE} + restart: always \ No newline at end of file diff --git a/go.mod b/go.mod index f574cc6..6a1eccf 100644 --- a/go.mod +++ b/go.mod @@ -10,16 +10,16 @@ require ( github.com/cockroachdb/errors v1.11.1 github.com/google/wire v0.6.0 github.com/smartystreets/goconvey v1.8.1 - github.com/spark-lence/tiga v0.0.0-20240523101823-47fd881052e5 + github.com/spark-lence/tiga v0.0.0-20240615083703-1e293faf6e11 github.com/spf13/cobra v1.8.0 - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 + google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 ) require ( github.com/allegro/bigcache/v3 v3.1.0 // indirect - golang.org/x/net v0.25.0 + golang.org/x/net v0.26.0 ) require ( @@ -43,15 +43,15 @@ require ( github.com/jcmturner/rpc/v2 v2.0.3 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/oapi-codegen/runtime v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/redis/go-redis/v9 v9.5.1 - github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/redis/go-redis/v9 v9.5.3 + github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/segmentio/kafka-go v0.4.47 // indirect github.com/sirupsen/logrus v1.9.3 @@ -59,35 +59,36 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.18.2 + github.com/spf13/viper v1.19.0 github.com/subosito/gotenv v1.6.0 // indirect github.com/thinkeridea/go-extend v1.3.2 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect - go.mongodb.org/mongo-driver v1.15.0 // indirect + go.mongodb.org/mongo-driver v1.15.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/sync v0.7.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/driver/mysql v1.5.6 // indirect + gorm.io/driver/mysql v1.5.7 // indirect gorm.io/gorm v1.25.10 ) require ( github.com/agiledragon/gomonkey/v2 v2.11.0 github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a - github.com/begonia-org/go-sdk v0.0.0-20240602084009-85eabb12d70e + github.com/begonia-org/go-sdk v0.0.0-20240615083549-ad7c60806f6a github.com/go-git/go-git/v5 v5.11.0 github.com/go-playground/validator/v10 v10.19.0 github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 + github.com/minio/minio-go/v7 v7.0.71 github.com/r3labs/sse/v2 v2.10.0 - go.etcd.io/etcd/api/v3 v3.5.13 - go.etcd.io/etcd/client/v3 v3.5.13 + go.etcd.io/etcd/api/v3 v3.5.14 + go.etcd.io/etcd/client/v3 v3.5.14 gopkg.in/cenkalti/backoff.v1 v1.1.0 ) @@ -103,6 +104,7 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect @@ -111,6 +113,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-redis/redismock/v9 v9.2.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -119,21 +122,24 @@ require ( github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/minio/md5-simd v1.1.2 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/sergi/go-diff v1.3.1 // indirect github.com/skeema/knownhosts v1.2.2 // indirect github.com/smarty/assertions v1.15.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index c99676e..306dc4e 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ github.com/agiledragon/gomonkey/v2 v2.11.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoa github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -23,10 +22,8 @@ github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa h1:DH github.com/begonia-org/go-layered-cache v0.0.0-20240510102605-41bdb7aa07fa/go.mod h1:xEqoca1vNGqH8CV7X9EzhDV5Ihtq9J95p7ZipzUB6pc= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a h1:Mpw7T+90KC5QW7yCa8Nn/5psnlvsexipAOrQAcc7YE0= github.com/begonia-org/go-loadbalancer v0.0.0-20240519060752-71ca464f0f1a/go.mod h1:crPS67sfgmgv47psftwfmTMbmTfdepVm8MPeqApINlI= -github.com/begonia-org/go-sdk v0.0.0-20240601175127-e9531218a7f3 h1:DLlhzfvYD81+QjEQqcgkKccS0nowjTU6wp/GCsLHT2M= -github.com/begonia-org/go-sdk v0.0.0-20240601175127-e9531218a7f3/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= -github.com/begonia-org/go-sdk v0.0.0-20240602084009-85eabb12d70e h1:R7xQlKsiWhWrR7ewCwpkHNYJqs1mXBoiI2+3TXD1qT0= -github.com/begonia-org/go-sdk v0.0.0-20240602084009-85eabb12d70e/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= +github.com/begonia-org/go-sdk v0.0.0-20240615083549-ad7c60806f6a h1:wOekXxD7AfUyptf49fMJqK2u+su6Wo7uCl5NxWM5bho= +github.com/begonia-org/go-sdk v0.0.0-20240615083549-ad7c60806f6a/go.mod h1:I70a3fiAADGrOoOC3lv408rFcTRhTwLt3pwr6cQwB4Y= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -59,6 +56,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= @@ -92,6 +91,8 @@ github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -117,7 +118,6 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -156,9 +156,11 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -170,6 +172,10 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.71 h1:No9XfOKTYi6i0GnBj+WZwD8WP5GZfL7n7GOjRqCdAjA= +github.com/minio/minio-go/v7 v7.0.71/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= @@ -194,15 +200,16 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= -github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= -github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU= +github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= +github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/segmentio/kafka-go v0.4.47 h1:IqziR4pA3vrZq7YdRxaT3w1/5fvIH5qpCwstUanQQB0= @@ -222,8 +229,8 @@ github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sS github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spark-lence/tiga v0.0.0-20240523101823-47fd881052e5 h1:lbs5Qf0cOLvIu0d1hftGNYWeudonk77XA6FRxa40yN8= -github.com/spark-lence/tiga v0.0.0-20240523101823-47fd881052e5/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= +github.com/spark-lence/tiga v0.0.0-20240615083703-1e293faf6e11 h1:D7LhqyeoUVS23NZmziKBNIW9jc3xvrBRg4fcwZPR4uM= +github.com/spark-lence/tiga v0.0.0-20240615083703-1e293faf6e11/go.mod h1:MSL8X9t+qvpQ4Tq3vVPKncq9RJcCzF2XGEWkCuNhm6Q= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= @@ -232,8 +239,8 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -266,14 +273,14 @@ github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76/go.mod h1:SQliXeA7Dh github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/etcd/api/v3 v3.5.13 h1:8WXU2/NBge6AUF1K1gOexB6e07NgsN1hXK0rSTtgSp4= -go.etcd.io/etcd/api/v3 v3.5.13/go.mod h1:gBqlqkcMMZMVTMm4NDZloEVJzxQOQIls8splbqBDa0c= -go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg= -go.etcd.io/etcd/client/pkg/v3 v3.5.13/go.mod h1:XxHT4u1qU12E2+po+UVPrEeL94Um6zL58ppuJWXSAB8= -go.etcd.io/etcd/client/v3 v3.5.13 h1:o0fHTNJLeO0MyVbc7I3fsCf6nrOqn5d+diSarKnB2js= -go.etcd.io/etcd/client/v3 v3.5.13/go.mod h1:cqiAeY8b5DEEcpxvgWKsbLIWNM/8Wy2xJSDMtioMcoI= -go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= -go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0= +go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU= +go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ= +go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= +go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= +go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= +go.mongodb.org/mongo-driver v1.15.1 h1:l+RvoUOoMXFmADTLfYDm7On9dRm7p4T80/lEQM+r7HU= +go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -290,10 +297,10 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -318,8 +325,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -348,8 +355,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -359,7 +366,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -371,8 +378,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -386,20 +393,19 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3 h1:QW9+G6Fir4VcRXVH8x3LilNAb6cxBGLa6+GM4hRwexE= +google.golang.org/genproto/googleapis/api v0.0.0-20240610135401-a8a62080eff3/go.mod h1:kdrSS/OiLkPrNUpzD4aHgCq2rVuC/YRxok32HXZ4vRE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -410,8 +416,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8= -gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= diff --git a/internal/biz/endpoint/endpoint.go b/internal/biz/endpoint/endpoint.go index 7c3d348..e18efdf 100644 --- a/internal/biz/endpoint/endpoint.go +++ b/internal/biz/endpoint/endpoint.go @@ -7,7 +7,6 @@ import ( "math" "time" - "github.com/begonia-org/begonia/internal/biz/file" "github.com/begonia-org/begonia/internal/pkg" "github.com/begonia-org/begonia/internal/pkg/config" loadbalance "github.com/begonia-org/go-loadbalancer" @@ -34,14 +33,13 @@ type EndpointRepo interface { type EndpointUsecase struct { repo EndpointRepo config *config.Config - file *file.FileUsecase snk *tiga.Snowflake watcher *EndpointWatcher } -func NewEndpointUsecase(repo EndpointRepo, file *file.FileUsecase, config *config.Config) *EndpointUsecase { +func NewEndpointUsecase(repo EndpointRepo, config *config.Config) *EndpointUsecase { snk, _ := tiga.NewSnowflake(1) - return &EndpointUsecase{repo: repo, file: file, config: config, snk: snk, watcher: NewWatcher(config, repo)} + return &EndpointUsecase{repo: repo, config: config, snk: snk, watcher: NewWatcher(config, repo)} } func (e *EndpointUsecase) AddConfig(ctx context.Context, srvConfig *api.EndpointSrvConfig) (string, error) { diff --git a/internal/biz/endpoint/endpoint_test.go b/internal/biz/endpoint/endpoint_test.go index 505b715..9b64029 100644 --- a/internal/biz/endpoint/endpoint_test.go +++ b/internal/biz/endpoint/endpoint_test.go @@ -44,7 +44,7 @@ func newEndpointBiz() *endpoint.EndpointUsecase { conf := config.ReadConfig(env) cnf := cfg.NewConfig(conf) repo := data.NewEndpointRepo(conf, gateway.Log) - return endpoint.NewEndpointUsecase(repo, nil, cnf) + return endpoint.NewEndpointUsecase(repo, cnf) } func testAddEndpoint(t *testing.T) { diff --git a/internal/biz/file/file.go b/internal/biz/file/file.go index ccb3479..a7cc37d 100644 --- a/internal/biz/file/file.go +++ b/internal/biz/file/file.go @@ -21,34 +21,80 @@ import ( common "github.com/begonia-org/go-sdk/common/api/v1" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing/object" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" "github.com/spark-lence/tiga" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/types/known/timestamppb" ) type FileRepo interface { // mysql - UploadFile(ctx context.Context, files []*common.Files) error - DeleteFile(ctx context.Context, files []*common.Files) error - UpdateFile(ctx context.Context, files []*common.Files) error - GetFile(ctx context.Context, uri string) (*common.Files, error) - ListFile(ctx context.Context, name []string) ([]*common.Files, error) + UpsertFile(ctx context.Context, file *api.Files) error + DelFile(ctx context.Context, engine, bucket, key string) error + UpsertBucket(ctx context.Context, bucket *api.Buckets) error + DelBucket(ctx context.Context, bucketId string) error + List(ctx context.Context, page, pageSize int32, bucket, engine, owner string) ([]*api.Files, error) } - -type FileUsecase struct { - // repo FileRepo +type FileUsecase interface { + Upload(ctx context.Context, in *api.UploadFileRequest, authorId string) (*api.UploadFileResponse, error) + InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) + UploadMultipartFileFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) + AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) + CompleteMultipartUploadFile(ctx context.Context, in *api.CompleteMultipartUploadRequest, authorId string) (*api.CompleteMultipartUploadResponse, error) + DownloadForRange(ctx context.Context, in *api.DownloadRequest, start int64, end int64, authorId string) ([]byte, int64, error) + Metadata(ctx context.Context, in *api.FileMetadataRequest, authorId string) (*api.FileMetadataResponse, error) + Version(ctx context.Context, bucket, key, authorId string) (string, error) + Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) + Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) + MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) + List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) +} +type FileUsecaseImpl struct { + repo FileRepo config *config.Config snowflake *tiga.Snowflake - // iam *service.ABACService } -func NewFileUsecase(config *config.Config) *FileUsecase { +func newMinioClient(cfg *config.Config) *minio.Client { + engines, err := cfg.GetFileEngines() + if err != nil { + panic(err) + + } + for _, engine := range engines { + if engine.Name == api.FileEngine_FILE_ENGINE_MINIO.String() { + endpoint := engine.Endpoint + creds := credentials.NewStaticV4(engine.GetAccessKey(), engine.SecretKey, "") + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: creds, + Secure: false, + }) + if err != nil { + panic(err) + } + return minioClient + } + } + return nil + +} +func NewFileUsecase(cfg *config.Config, repo FileRepo) map[string]FileUsecase { + engines := make(map[string]FileUsecase) + if minioClient := newMinioClient(cfg); minioClient != nil { + engines[api.FileEngine_FILE_ENGINE_MINIO.String()] = NewMinioUseCase(minioClient, NewLocalFileUsecase(cfg, repo)) + } + engines[api.FileEngine_FILE_ENGINE_LOCAL.String()] = NewLocalFileUsecase(cfg, repo) + return engines +} +func NewLocalFileUsecase(config *config.Config, repo FileRepo) *FileUsecaseImpl { snk, _ := tiga.NewSnowflake(1) - return &FileUsecase{config: config, snowflake: snk} + return &FileUsecaseImpl{config: config, snowflake: snk, repo: repo} } -func (f *FileUsecase) getPartsDir(key string) string { +func (f *FileUsecaseImpl) getPartsDir(key string) string { return filepath.Join(f.config.GetUploadDir(), key, "parts") } -func (f *FileUsecase) InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { +func (f *FileUsecaseImpl) InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { if in.Key == "" || strings.HasPrefix(in.Key, "/") { return nil, gosdk.NewError(pkg.ErrInvalidFileKey, int32(api.FileSvrStatus_FILE_INVALIDATE_KEY_ERR), codes.InvalidArgument, "invalid_key") } @@ -78,7 +124,7 @@ func getSHA256(data []byte) string { } // newVersionFile 检查指定目录的仓库状态,适当时初始化仓库,并提交文件 -func (f *FileUsecase) commitFile(dir string, filename string, authorId string, authorEmail string) (commitId string, err error) { +func (f *FileUsecaseImpl) commitFile(dir string, filename string, authorId string, authorEmail string) (commitId string, err error) { repo, err := git.PlainInit(dir, false) if err != nil && err != git.ErrRepositoryAlreadyExists { return "", err @@ -123,23 +169,24 @@ func (f *FileUsecase) commitFile(dir string, filename string, authorId string, a commit, err := w.Commit(fmt.Sprintf("Add %s", filename), &git.CommitOptions{ Author: author, }) - if err != nil { - return "", err - } - - // 打印新提交的ID - obj, err := repo.CommitObject(commit) if err != nil && !goErr.Is(err, git.ErrEmptyCommit) { return "", err } // 空提交处理 if goErr.Is(err, git.ErrEmptyCommit) { + headRef, err := repo.Head() if err != nil || headRef.Hash().IsZero() { return "", fmt.Errorf("get head ref error:%w or head ref is nil", err) } return headRef.Hash().String(), nil } + // 打印新提交的ID + obj, err := repo.CommitObject(commit) + + if err != nil { + return "", err + } return obj.ID().String(), nil } @@ -147,9 +194,9 @@ func (f *FileUsecase) commitFile(dir string, filename string, authorId string, a // getSaveDir Get the save directory of the file // // The save directory is the directory where the file is saved. -func (f *FileUsecase) getSaveDir(key string) string { +func (f *FileUsecaseImpl) getSaveDir(bucket, key string) string { - saveDir := filepath.Join(f.config.GetUploadDir(), filepath.Dir(key)) + saveDir := filepath.Join(f.config.GetUploadDir(), bucket, filepath.Dir(key)) return saveDir @@ -159,19 +206,30 @@ func (f *FileUsecase) getSaveDir(key string) string { // // If the key is empty or starts with '/', it returns an error. // The key is not allow start with '/'. -func (f *FileUsecase) checkIn(key string) (string, error) { +func (f *FileUsecaseImpl) checkIn(key string) (string, error) { if key == "" || strings.HasPrefix(key, "/") { return "", gosdk.NewError(pkg.ErrInvalidFileKey, int32(api.FileSvrStatus_FILE_INVALIDATE_KEY_ERR), codes.InvalidArgument, "invalid_key") } return key, nil } +func (f *FileUsecaseImpl) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { + saveDir := filepath.Join(f.config.GetUploadDir(), in.Bucket) + if err := os.MkdirAll(saveDir, 0755); err != nil { + return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket") + } + return &api.MakeBucketResponse{}, nil +} +func (f *FileUsecaseImpl) checkBucket(bucket string) bool { + saveDir := filepath.Join(f.config.GetUploadDir(), bucket) + return pathExists(saveDir) +} // Upload uploads a file. // // The file is saved in the directory specified by the key. // // The authorId is used to determine the directory which is as user's home dir where the file is saved. -func (f *FileUsecase) Upload(ctx context.Context, in *api.UploadFileRequest, authorId string) (*api.UploadFileResponse, error) { +func (f *FileUsecaseImpl) Upload(ctx context.Context, in *api.UploadFileRequest, authorId string) (*api.UploadFileResponse, error) { if authorId == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } @@ -183,8 +241,11 @@ func (f *FileUsecase) Upload(ctx context.Context, in *api.UploadFileRequest, aut in.Key = filepath.Join(authorId, key) // log.Printf("key:%s", in.Key) filename := filepath.Base(in.Key) - // in.Key = key - saveDir := f.getSaveDir(in.Key) + if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok { + return nil, gosdk.NewError(pkg.ErrBucketNotFound, int32(api.FileSvrStatus_FILE_INVALIDATE_BUCKET_ERR), codes.NotFound, "bucket_not_found") + + } + saveDir := f.getSaveDir(in.Bucket, in.Key) if err := os.MkdirAll(saveDir, 0755); err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "create_upload_dir") @@ -204,7 +265,7 @@ func (f *FileUsecase) Upload(ctx context.Context, in *api.UploadFileRequest, aut if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "write_file") } - uri, err := f.getUri(filePath) + uri, err := f.getUri(in.Bucket, filePath) if err != nil { return nil, err } @@ -224,14 +285,26 @@ func (f *FileUsecase) Upload(ctx context.Context, in *api.UploadFileRequest, aut } } - + err = f.repo.UpsertFile(ctx, &api.Files{ + Uid: f.snowflake.GenerateIDString(), + Engine: api.FileEngine_name[int32(api.FileEngine_FILE_ENGINE_LOCAL)], + Bucket: in.Bucket, + Key: uri, + IsDeleted: false, + Owner: authorId, + CreatedAt: timestamppb.Now(), + UpdatedAt: timestamppb.Now(), + }) + if err != nil { + return nil, err + } return &api.UploadFileResponse{ Uri: uri, Version: commitId, }, err } -func (f *FileUsecase) UploadMultipartFileFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) { +func (f *FileUsecaseImpl) UploadMultipartFileFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) { if in.UploadId == "" { return nil, gosdk.NewError(pkg.ErrUploadIdMissing, int32(api.FileSvrStatus_FILE_UPLOADID_MISSING_ERR), codes.InvalidArgument, "upload_id_not_found") @@ -268,15 +341,15 @@ func (f *FileUsecase) UploadMultipartFileFile(ctx context.Context, in *api.Uploa return nil, err } - uri, err := f.getUri(filePath) - if err != nil { - return nil, err - } + // uri, err := f.getUri(in.,filePath) + // if err != nil { + // return nil, err + // } return &api.UploadMultipartFileResponse{ - Uri: uri, + // Uri: uri, }, nil } -func (f *FileUsecase) getSortedFiles(dirPath string) ([]string, error) { +func (f *FileUsecaseImpl) getSortedFiles(dirPath string) ([]string, error) { var files []string err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { @@ -291,13 +364,10 @@ func (f *FileUsecase) getSortedFiles(dirPath string) ([]string, error) { if err != nil { return nil, err } - - // 按文件名排序 sort.Strings(files) - return files, nil } -func (f FileUsecase) mergeFiles(files []string, outputFile string) error { +func (f *FileUsecaseImpl) mergeFiles(files []string, outputFile string) error { baseDir := filepath.Dir(outputFile) if err := os.MkdirAll(baseDir, 0755); err != nil { return err @@ -322,7 +392,7 @@ func (f FileUsecase) mergeFiles(files []string, outputFile string) error { return nil } -func (f *FileUsecase) mvDir(src, dst string) error { +func (f *FileUsecaseImpl) mvDir(src, dst string) error { newPath := filepath.Join(dst, filepath.Base(src)) if _, err := os.Stat(newPath); err == nil { // 目标路径存在,尝试删除 @@ -333,24 +403,24 @@ func (f *FileUsecase) mvDir(src, dst string) error { return os.Rename(src, newPath) } -func (f *FileUsecase) getPersistenceKeyParts(key string) string { +func (f *FileUsecaseImpl) getPersistenceKeyParts(key string) string { if strings.Contains(key, ".") { key = key[:strings.LastIndex(key, ".")] } return filepath.Join(f.config.GetUploadDir(), "parts", key) } -func (f *FileUsecase) getUri(filePath string) (string, error) { +func (f *FileUsecaseImpl) getUri(bucket, filePath string) (string, error) { uploadRootDir := f.config.GetUploadDir() // log.Printf("uploadRootDir:%s,filePath:%s", uploadRootDir, filePath) - uri, err := filepath.Rel(uploadRootDir, filePath) + uri, err := filepath.Rel(filepath.Join(uploadRootDir, bucket), filePath) if err != nil { return "", gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_file_uri") } return uri, nil } -func (f *FileUsecase) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { +func (f *FileUsecaseImpl) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { partsDir := f.getPartsDir(in.UploadId) if !pathExists(partsDir) { err := gosdk.NewError(pkg.ErrUploadIdNotFound, int32(api.FileSvrStatus_FILE_NOT_FOUND_UPLOADID_ERR), codes.NotFound, "upload_id_not_found") @@ -363,7 +433,7 @@ func (f *FileUsecase) AbortMultipartUpload(ctx context.Context, in *api.AbortMul } return &api.AbortMultipartUploadResponse{}, nil } -func (f *FileUsecase) CompleteMultipartUploadFile(ctx context.Context, in *api.CompleteMultipartUploadRequest, authorId string) (*api.CompleteMultipartUploadResponse, error) { +func (f *FileUsecaseImpl) CompleteMultipartUploadFile(ctx context.Context, in *api.CompleteMultipartUploadRequest, authorId string) (*api.CompleteMultipartUploadResponse, error) { if authorId == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } @@ -371,6 +441,10 @@ func (f *FileUsecase) CompleteMultipartUploadFile(ctx context.Context, in *api.C if err != nil { return nil, err } + if ok := f.checkBucket(in.Bucket); in.Bucket == "" || !ok { + return nil, gosdk.NewError(pkg.ErrBucketNotFound, int32(api.FileSvrStatus_FILE_INVALIDATE_BUCKET_ERR), codes.NotFound, "bucket_not_found") + + } in.Key = filepath.Join(authorId, key) partsDir := f.getPartsDir(in.UploadId) if !pathExists(partsDir) { @@ -380,14 +454,14 @@ func (f *FileUsecase) CompleteMultipartUploadFile(ctx context.Context, in *api.C } files, err := f.getSortedFiles(partsDir) if err != nil { + return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_sorted_files") } - saveDir := f.getSaveDir(in.Key) + saveDir := f.getSaveDir(in.Bucket, in.Key) filename := filepath.Base(in.Key) filePath := filepath.Join(saveDir, filename) - // merge files to uploadDir/key err = f.mergeFiles(files, filePath) if err != nil { return nil, gosdk.NewError(fmt.Errorf("merge file error:%w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "merge_files") @@ -402,7 +476,7 @@ func (f *FileUsecase) CompleteMultipartUploadFile(ctx context.Context, in *api.C return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "mv_dir") } - uri, err := f.getUri(filePath) + uri, err := f.getUri(in.Bucket, filePath) if err != nil { return nil, err @@ -414,6 +488,20 @@ func (f *FileUsecase) CompleteMultipartUploadFile(ctx context.Context, in *api.C return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "commit_file") } } + + err = f.repo.UpsertFile(ctx, &api.Files{ + Uid: f.snowflake.GenerateIDString(), + Engine: in.Engine, + Bucket: in.Bucket, + Key: uri, + IsDeleted: false, + Owner: authorId, + CreatedAt: timestamppb.Now(), + UpdatedAt: timestamppb.Now(), + }) + if err != nil { + return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") + } os.RemoveAll(filepath.Join(f.config.GetUploadDir(), in.UploadId)) return &api.CompleteMultipartUploadResponse{ @@ -422,7 +510,7 @@ func (f *FileUsecase) CompleteMultipartUploadFile(ctx context.Context, in *api.C }, err } -func (f *FileUsecase) DownloadForRange(ctx context.Context, in *api.DownloadRequest, start int64, end int64, authorId string) ([]byte, int64, error) { +func (f *FileUsecaseImpl) DownloadForRange(ctx context.Context, in *api.DownloadRequest, start int64, end int64, authorId string) ([]byte, int64, error) { key, err := f.checkIn(in.Key) if err != nil { return nil, 0, err @@ -434,7 +522,7 @@ func (f *FileUsecase) DownloadForRange(ctx context.Context, in *api.DownloadRequ } - file, err := f.getReader(in.Key, in.Version) + file, err := f.getReader(in.Bucket, in.Key, in.Version) if err != nil { code, grcpCode := f.checkStatusCode(err) return nil, 0, gosdk.NewError(err, code, grcpCode, "open_file") @@ -455,14 +543,14 @@ func (f *FileUsecase) DownloadForRange(ctx context.Context, in *api.DownloadRequ return buf, file.Size(), nil } -func (f *FileUsecase) Metadata(ctx context.Context, in *api.FileMetadataRequest, authorId string) (*api.FileMetadataResponse, error) { +func (f *FileUsecaseImpl) Metadata(ctx context.Context, in *api.FileMetadataRequest, authorId string) (*api.FileMetadataResponse, error) { key, err := f.checkIn(in.Key) originKey := in.Key if err != nil { return nil, err } in.Key = key - file, err := f.getReader(in.Key, in.Version) + file, err := f.getReader(in.Bucket, in.Key, in.Version) if err != nil { code, grpcCode := f.checkStatusCode(err) return nil, gosdk.NewError(err, code, grpcCode, "open_file") @@ -493,7 +581,7 @@ func (f *FileUsecase) Metadata(ctx context.Context, in *api.FileMetadataRequest, if versionReader, ok := file.(FileVersionReader); ok { version = versionReader.Version() } else { - version, _ = f.Version(ctx, originKey, authorId) + version, _ = f.Version(ctx, in.Bucket, originKey, authorId) } return &api.FileMetadataResponse{ @@ -510,9 +598,9 @@ func (f *FileUsecase) Metadata(ctx context.Context, in *api.FileMetadataRequest, // getReader obtains a file reader. // // If the version is not empty, the file reader will be a version file reader. -func (f *FileUsecase) getReader(key string, version string) (FileReader, error) { +func (f *FileUsecaseImpl) getReader(bucket, key string, version string) (FileReader, error) { - dir := f.getSaveDir(key) + dir := f.getSaveDir(bucket, key) filePath := filepath.Join(dir, filepath.Base(key)) var fileReader FileReader @@ -532,12 +620,12 @@ func (f *FileUsecase) getReader(key string, version string) (FileReader, error) return fileReader, nil } -func (f *FileUsecase) Version(ctx context.Context, key, authorId string) (string, error) { +func (f *FileUsecaseImpl) Version(ctx context.Context, bucket, key, authorId string) (string, error) { key, err := f.checkIn(key) if err != nil { return "", err } - file, err := f.getReader(key, "latest") + file, err := f.getReader(bucket, key, "latest") if err != nil { code, grpcCode := f.checkStatusCode(err) return "", gosdk.NewError(err, code, grpcCode, "open_file") @@ -545,7 +633,7 @@ func (f *FileUsecase) Version(ctx context.Context, key, authorId string) (string defer file.Close() return file.(FileVersionReader).Version(), nil } -func (f *FileUsecase) checkStatusCode(err error) (int32, codes.Code) { +func (f *FileUsecaseImpl) checkStatusCode(err error) (int32, codes.Code) { switch err { case git.ErrRepositoryNotExists, os.ErrNotExist: @@ -554,14 +642,14 @@ func (f *FileUsecase) checkStatusCode(err error) (int32, codes.Code) { return int32(common.Code_INTERNAL_ERROR), codes.Internal } } -func (f *FileUsecase) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) { +func (f *FileUsecaseImpl) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) { key, err := f.checkIn(in.Key) if err != nil { return nil, err } in.Key = key - file, err := f.getReader(in.Key, in.Version) + file, err := f.getReader(in.Bucket, in.Key, in.Version) if err != nil { code, httpCode := f.checkStatusCode(err) return nil, gosdk.NewError(err, code, httpCode, "open_file") @@ -580,14 +668,13 @@ func (f *FileUsecase) Download(ctx context.Context, in *api.DownloadRequest, aut return buf, nil } - -func (f *FileUsecase) Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) { +func (f *FileUsecaseImpl) Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) { key, err := f.checkIn(in.Key) if err != nil { return nil, err } in.Key = key - file, err := f.getReader(in.Key, "") + file, err := f.getReader(in.Bucket, in.Key, "") if err != nil && !os.IsNotExist(err) { // log.Printf("err:%v", err) return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "remove_file") @@ -597,7 +684,7 @@ func (f *FileUsecase) Delete(ctx context.Context, in *api.DeleteRequest, authorI defer file.Close() os.Remove(file.Name()) } - versionFile, err := f.getReader(in.Key, "latest") + versionFile, err := f.getReader(in.Bucket, in.Key, "latest") if err != nil { // log.Printf("version err:%v", err) code, rpcCode := f.checkStatusCode(err) @@ -611,5 +698,18 @@ func (f *FileUsecase) Delete(ctx context.Context, in *api.DeleteRequest, authorI if err != nil { return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "remove_parts_dir") } - return &api.DeleteResponse{}, nil + err = f.repo.DelFile(ctx, in.Bucket, in.Key, in.Key) + if err != nil { + return nil, gosdk.NewError(err, int32(common.Code_INTERNAL_ERROR), codes.Internal, "remove_file") + + } + return &api.DeleteResponse{}, err +} + +func (f *FileUsecaseImpl) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) { + files, err := f.repo.List(ctx, in.Page, int32(in.PageSize), in.Bucket, in.Engine, authorId) + if err != nil { + return nil, err + } + return files, nil } diff --git a/internal/biz/file/file_test.go b/internal/biz/file/file_test.go index d11edf8..dbc3605 100644 --- a/internal/biz/file/file_test.go +++ b/internal/biz/file/file_test.go @@ -18,12 +18,15 @@ import ( "github.com/agiledragon/gomonkey/v2" "github.com/begonia-org/begonia" "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" "github.com/begonia-org/begonia/internal/biz/file" + "github.com/begonia-org/begonia/internal/data" "github.com/begonia-org/begonia/internal/pkg" api "github.com/begonia-org/go-sdk/api/file/v1" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" + "github.com/spark-lence/tiga" cfg "github.com/begonia-org/begonia/internal/pkg/config" c "github.com/smartystreets/goconvey/convey" @@ -43,6 +46,7 @@ type fileUploadTestCase struct { expectPath string title string author string + bucket string expectUri string expectErr error } @@ -60,6 +64,7 @@ type FileDownloadTestCase struct { var fileAuthor = "" var fileSha256 = "" var uploadId = "" +var bucket = "" var versions map[string]string = map[string]string{} var tmp *tmpFile = nil var tmp3 *tmpFile = nil @@ -119,16 +124,39 @@ func generateRandomFile(size int64) (*tmpFile, error) { // return tmpfile.Name(), hashValue, contentType, nil } -func newFileBiz() *file.FileUsecase { +func newFileBiz() *file.FileUsecaseImpl { env := "dev" if begonia.Env != "" { env = begonia.Env } - config := config.ReadConfig(env) - cnf := cfg.NewConfig(config) - return file.NewFileUsecase(cnf) + cnf := config.ReadConfig(env) + conf := cfg.NewConfig(cnf) + repo := data.NewFileRepo(cnf, gateway.Log) + return file.NewLocalFileUsecase(conf, repo) } +func testMkBucket(t *testing.T) { + fileBiz := newFileBiz() + snk, _ := tiga.NewSnowflake(1) + fileAuthor = snk.GenerateIDString() + bucket = fmt.Sprintf("bucket-biz-%s", time.Now().Format("20060102150405")) + c.Convey("test make bucket success", t, func() { + rsp, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ + Bucket: bucket, + }) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + }) + c.Convey("test make bucket fail", t, func() { + patch := gomonkey.ApplyFuncReturn(os.MkdirAll, fmt.Errorf("mkdir error")) + defer patch.Reset() + _, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ + Bucket: "", + }) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "mkdir error") + }) +} func testPutFile(t *testing.T) { fileBiz := newFileBiz() var err error @@ -156,69 +184,66 @@ func testPutFile(t *testing.T) { } config := config.ReadConfig(env) cnf := cfg.NewConfig(config) - fileAuthor = fmt.Sprintf("tester-%s", time.Now().Format("20060102150405")) fileSha256 = tmp.sha256 + // bucket = fmt.Sprintf("bucket-biz-%s", time.Now().Format("20060102150405")) + cases := []fileUploadTestCase{ { title: "test upload without version", tmp: tmp, useVersion: false, key: "test/upload.test1", - expectPath: filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test1"), + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test1"), expectUri: fileAuthor + "/test/upload.test1", expectErr: nil, author: fileAuthor, + bucket: bucket, }, { title: "test upload with version", tmp: tmp, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2"), + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), expectUri: fileAuthor + "/test/upload.test2", expectErr: nil, author: fileAuthor, + bucket: bucket, }, { title: "test upload with version1", tmp: tmp, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2"), + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), expectUri: fileAuthor + "/test/upload.test2", expectErr: nil, author: fileAuthor, + bucket: bucket, }, { title: "test upload with version2", tmp: tmp3, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2"), + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), expectUri: fileAuthor + "/test/upload.test2", expectErr: nil, author: fileAuthor, + bucket: bucket, }, { title: "test upload with version3", tmp: tmp, useVersion: true, key: "test/upload.test2", - expectPath: filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2"), + expectPath: filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), expectUri: fileAuthor + "/test/upload.test2", expectErr: nil, author: fileAuthor, + bucket: bucket, }, - // { - // title: "test upload with version4", - // tmp: tmp, - // useVersion: true, - // key: "test/upload.test2", - // expectPath: filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2"), - // expectUri: fileAuthor + "/test/upload.test2", - // expectErr: nil, - // author: fileAuthor, - // }, + { title: "test upload with invalid key", tmp: tmp, @@ -227,6 +252,7 @@ func testPutFile(t *testing.T) { expectUri: "", expectErr: pkg.ErrInvalidFileKey, author: fileAuthor, + bucket: bucket, }, { title: "test upload with invalid author", @@ -236,6 +262,7 @@ func testPutFile(t *testing.T) { expectUri: "", expectErr: pkg.ErrIdentityMissing, author: "", + bucket: bucket, }, { title: "test upload fail with not match sha256", @@ -245,9 +272,19 @@ func testPutFile(t *testing.T) { expectUri: "", expectErr: pkg.ErrSHA256NotMatch, author: fileAuthor, + bucket: bucket, + }, + { + title: "test upload fail with not match sha256", + tmp: tmp2, + key: "test/upload.test7", + expectPath: "", + expectUri: "", + expectErr: pkg.ErrBucketNotFound, + author: fileAuthor, + bucket: fmt.Sprintf("bucket-biz-err-%s", time.Now().Format("20060102150405")), }, } - for _, v := range cases { _case := v c.Convey(_case.title, t, func() { @@ -257,6 +294,7 @@ func testPutFile(t *testing.T) { ContentType: _case.tmp.contentType, UseVersion: _case.useVersion, Sha256: _case.tmp.sha256, + Bucket: _case.bucket, }, _case.author) if _case.expectErr != nil { @@ -287,7 +325,7 @@ func testPutFile(t *testing.T) { } fileAuthor2 := fmt.Sprintf("tester-2-%s", time.Now().Format("20060102150405")) - filePath := filepath.Join(cnf.GetUploadDir(), fileAuthor2) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor2) file, err := os.Create(filePath) if err != nil { t.Error(err) @@ -299,6 +337,7 @@ func testPutFile(t *testing.T) { ContentType: tmp.contentType, UseVersion: true, Sha256: tmp.sha256, + Bucket: bucket, }, fileAuthor2) c.So(err, c.ShouldNotBeNil) @@ -311,6 +350,7 @@ func testPutFile(t *testing.T) { ContentType: tmp.contentType, UseVersion: true, Sha256: tmp.sha256, + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) @@ -325,6 +365,7 @@ func testPutFile(t *testing.T) { ContentType: tmp.contentType, UseVersion: true, Sha256: tmp.sha256, + Bucket: bucket, }, fileAuthor3) patch.Reset() c.So(err, c.ShouldNotBeNil) @@ -339,6 +380,7 @@ func testPutFile(t *testing.T) { ContentType: tmp.contentType, UseVersion: true, Sha256: tmp.sha256, + Bucket: bucket, }, fileAuthor3) c.So(err, c.ShouldNotBeNil) c.So(rsp, c.ShouldBeNil) @@ -399,15 +441,7 @@ func testDownload(t *testing.T) { author: fileAuthor, sha256: tmp.sha256, }, - // { - // title: "download fail with invalidate author", - // exceptErr: pkg.ErrIdentityMissing, - // key: fileAuthor + "/test/upload.test1", - // useVersion: true, - // version: "", - // author: "", - // sha256: tmp.sha256, - // }, + { title: "download fail with invalidate key", exceptErr: fmt.Errorf("%s", "no such file or directory"), @@ -432,6 +466,7 @@ func testDownload(t *testing.T) { c.Convey(_case.title, t, func() { rsp, err := fileBiz.Download(context.TODO(), &api.DownloadRequest{ Key: _case.key, + Bucket: bucket, Version: _case.version, }, _case.author) if _case.exceptErr != nil { @@ -450,7 +485,8 @@ func testDownload(t *testing.T) { } c.Convey("test download fail", t, func() { _, err := fileBiz.Download(context.TODO(), &api.DownloadRequest{ - Key: "/" + fileAuthor + "/test/upload.test1", + Key: "/" + fileAuthor + "/test/upload.test1", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) @@ -462,7 +498,7 @@ func testDownload(t *testing.T) { } config := config.ReadConfig(env) cnf := cfg.NewConfig(config) - filePath := filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2") + filePath := filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2") t.Logf("filepath:%s", filePath) reader, err := file.NewFileVersionReader(filePath, "latest") c.So(err, c.ShouldBeNil) @@ -470,12 +506,13 @@ func testDownload(t *testing.T) { defer patch.Reset() _, err = fileBiz.Download(context.TODO(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.test2", + Bucket: bucket, Version: "latest", }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "reader error") patch.Reset() - file, err := file.NewFileVersionReader(filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.test2"), "latest") + file, err := file.NewFileVersionReader(filepath.Join(cnf.GetUploadDir(), bucket, fileAuthor, "test", "upload.test2"), "latest") c.So(err, c.ShouldBeNil) ioReader, err := file.Reader() c.So(err, c.ShouldBeNil) @@ -484,6 +521,7 @@ func testDownload(t *testing.T) { _, err = fileBiz.Download(context.TODO(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.test2", Version: "latest", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) @@ -679,19 +717,7 @@ func testUploadMultipartFileFile(t *testing.T) { c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "write error") patch2.Reset() - patch3 := gomonkey.ApplyFuncReturn(filepath.Rel, "", fmt.Errorf("rel error")) - defer patch3.Reset() - _, err = fileBiz.UploadMultipartFileFile(context.TODO(), &api.UploadMultipartFileRequest{ - Key: "test/upload.parts.test1", - UploadId: rsp.UploadId, - PartNumber: 1, - Content: tmpFile2.content, - Sha256: tmpFile2.sha256, - }) - c.So(err, c.ShouldNotBeNil) - c.So(err.Error(), c.ShouldContainSubstring, "rel error") - patch3.Reset() }) } @@ -744,13 +770,14 @@ func testCompleteMultipartUploadFile(t *testing.T) { UploadId: uploadId, UseVersion: true, Sha256: bigFileSha256, + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) c.So(rsp.Uri, c.ShouldNotBeEmpty) uploadRootDir := cnf.GetUploadDir() - filePath := filepath.Join(uploadRootDir, rsp.Uri) + filePath := filepath.Join(uploadRootDir, bucket, rsp.Uri) sha, err := sumFileSha256(filePath) if err != nil { @@ -764,6 +791,7 @@ func testCompleteMultipartUploadFile(t *testing.T) { _, err := fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ Key: "test/upload.parts.test2", UploadId: "123455678098", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrUploadIdNotFound.Error()) @@ -778,6 +806,7 @@ func testCompleteMultipartUploadFile(t *testing.T) { _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ Key: "/test/upload.parts.test2", UploadId: "123455678098", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) @@ -792,6 +821,7 @@ func testCompleteMultipartUploadFile(t *testing.T) { Key: "test/upload.parts.test2", UploadId: uploadId2, UseVersion: true, + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "lstat error") @@ -850,6 +880,7 @@ func testCompleteMultipartUploadFile(t *testing.T) { Key: "test/upload.parts.test2", UploadId: uploadId3, UseVersion: true, + Bucket: bucket, }, fileAuthor) patch2.Reset() @@ -866,6 +897,7 @@ func testFileMeta(t *testing.T) { meta, err := fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(meta, c.ShouldNotBeNil) @@ -875,6 +907,7 @@ func testFileMeta(t *testing.T) { meta, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldBeNil) c.So(meta, c.ShouldNotBeNil) @@ -885,6 +918,7 @@ func testFileMeta(t *testing.T) { _, err := fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ Key: "", Version: "", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) @@ -894,7 +928,7 @@ func testFileMeta(t *testing.T) { } conf := config.ReadConfig(env) cnf := cfg.NewConfig(conf) - filePath := filepath.Join(cnf.GetUploadDir(), filepath.Dir(fileAuthor+"/test/upload.parts.test1")) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir(fileAuthor+"/test/upload.parts.test1")) filePath = filepath.Join(filePath, filepath.Base(fileAuthor+"/test/upload.parts.test1")) patch := gomonkey.ApplyFuncReturn(file.NewFileVersionReader, nil, fmt.Errorf("file NewFileVersionReader error")) @@ -902,6 +936,7 @@ func testFileMeta(t *testing.T) { _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file NewFileVersionReader error") @@ -914,6 +949,7 @@ func testFileMeta(t *testing.T) { _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file Reader error") @@ -922,7 +958,7 @@ func testFileMeta(t *testing.T) { patch3 := gomonkey.ApplyFuncReturn(io.Copy, int64(0), fmt.Errorf("io.copy error")) defer patch3.Reset() _, err = fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{Key: fileAuthor + "/test/upload.parts.test1", - Version: "latest"}, fileAuthor) + Version: "latest", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "io.copy error") @@ -937,7 +973,7 @@ func testVersionReader(t *testing.T) { cnf := cfg.NewConfig(config) c.Convey("test version reader", t, func() { root := cnf.GetUploadDir() - path := filepath.Join(root, fileAuthor, "test", "upload.parts.test1") + path := filepath.Join(root, bucket, fileAuthor, "test", "upload.parts.test1") reader, err := file.NewFileVersionReader(path, "") c.So(err, c.ShouldBeNil) c.So(reader, c.ShouldNotBeNil) @@ -951,12 +987,12 @@ func testVersionReader(t *testing.T) { }) c.Convey("test version reader fail", t, func() { fileBiz := newFileBiz() - _, err := fileBiz.Version(context.Background(), "/test/version.test", fileAuthor) + _, err := fileBiz.Version(context.Background(), bucket, "/test/version.test", fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) patch := gomonkey.ApplyFuncReturn(file.NewFileVersionReader, nil, fmt.Errorf("file NewFileVersionReader error")) defer patch.Reset() - _, err = fileBiz.Version(context.Background(), fileAuthor+"/test/upload.parts.test1", fileAuthor) + _, err = fileBiz.Version(context.Background(), bucket, fileAuthor+"/test/upload.parts.test1", fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file NewFileVersionReader error") patch.Reset() @@ -970,13 +1006,13 @@ func testVersionReader(t *testing.T) { patch3 := gomonkey.ApplyFuncReturn((*git.Repository).Head, nil, fmt.Errorf("head error")) defer patch3.Reset() root := cnf.GetUploadDir() - path := filepath.Join(root, fileAuthor, "test", "upload.parts.test2") + path := filepath.Join(root, bucket, fileAuthor, "test", "upload.parts.test2") _, err = file.NewFileVersionReader(path, "latest") c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "head error") patch3.Reset() - filePath := filepath.Join(cnf.GetUploadDir(), filepath.Dir(fileAuthor+"/test/upload.parts.test1")) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir(fileAuthor+"/test/upload.parts.test1")) filePath = filepath.Join(filePath, filepath.Base(fileAuthor+"/test/upload.parts.test1")) reader, err := file.NewFileVersionReader(filePath, "latest") c.So(err, c.ShouldBeNil) @@ -1042,6 +1078,7 @@ func testDownloadRange(t *testing.T) { data, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, rangeStartAt, rangeEndAt, fileAuthor) c.So(err, c.ShouldBeNil) shaer.Write(data) @@ -1053,6 +1090,7 @@ func testDownloadRange(t *testing.T) { data, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, 0, 0, fileAuthor) c.So(err, c.ShouldBeNil) shaer2.Write(data) @@ -1062,6 +1100,7 @@ func testDownloadRange(t *testing.T) { data, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "", + Bucket: bucket, }, 0, 0, fileAuthor) c.So(err, c.ShouldBeNil) shaer3.Write(data) @@ -1071,6 +1110,7 @@ func testDownloadRange(t *testing.T) { _, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.parts.test40", Version: "lasted", + Bucket: bucket, }, 0, 1024, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, plumbing.ErrObjectNotFound.Error()) @@ -1084,6 +1124,7 @@ func testDownloadRange(t *testing.T) { _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: "test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, 1024, 1, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidRange.Error()) @@ -1091,6 +1132,7 @@ func testDownloadRange(t *testing.T) { _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, 1024, 0, fileAuthor) c.So(err, c.ShouldBeNil) patch := gomonkey.ApplyFuncReturn(file.NewFileVersionReader, nil, git.ErrRepositoryNotExists) @@ -1098,6 +1140,7 @@ func testDownloadRange(t *testing.T) { _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: "test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, 0, 1024, fileAuthor) patch.Reset() c.So(err, c.ShouldNotBeNil) @@ -1109,7 +1152,7 @@ func testDownloadRange(t *testing.T) { } conf := config.ReadConfig(env) cnf := cfg.NewConfig(conf) - filePath := filepath.Join(cnf.GetUploadDir(), filepath.Dir(fileAuthor+"/test/upload.parts.test1")) + filePath := filepath.Join(cnf.GetUploadDir(), bucket, filepath.Dir(fileAuthor+"/test/upload.parts.test1")) filePath = filepath.Join(filePath, filepath.Base(fileAuthor+"/test/upload.parts.test1")) reader, err := file.NewFileVersionReader(filePath, "latest") c.So(err, c.ShouldBeNil) @@ -1119,6 +1162,7 @@ func testDownloadRange(t *testing.T) { _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ Key: fileAuthor + "/test/upload.parts.test1", Version: "latest", + Bucket: bucket, }, 0, 1024, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file readAt error") @@ -1126,6 +1170,32 @@ func testDownloadRange(t *testing.T) { }) } +func testList(t *testing.T) { + fileBiz := newFileBiz() + c.Convey("test list file success", t, func() { + rsp, err := fileBiz.List(context.Background(), &api.ListFilesRequest{ + Bucket: bucket, + Page: 1, + PageSize: 20, + }, fileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + c.So(rsp, c.ShouldNotBeEmpty) + }) + c.Convey("test list file fail", t, func() { + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.Pagination, fmt.Errorf("file not found")) + defer patch.Reset() + _, err := fileBiz.List(context.Background(), &api.ListFilesRequest{ + Bucket: fmt.Sprintf("test-%s", time.Now().Format("20060102150405")), + Page: -1, + PageSize: 20, + }, fileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "file not found") + }) + +} func testDelete(t *testing.T) { fileBiz := newFileBiz() env := "dev" @@ -1138,22 +1208,22 @@ func testDelete(t *testing.T) { c.Convey("test delete file fail", t, func() { patch := gomonkey.ApplyFuncReturn(file.NewFileReader, nil, fmt.Errorf("file not found")) defer patch.Reset() - _, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.deleted"}, fileAuthor) + _, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.deleted", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "file not found") patch.Reset() - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.deleted2"}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "not found") - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "/" + fileAuthor + "/test/upload.parts.deleted2"}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "/" + fileAuthor + "/test/upload.parts.deleted2", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidFileKey.Error()) patch2 := gomonkey.ApplyFuncReturn(os.RemoveAll, fmt.Errorf("remove all error")) defer patch2.Reset() - _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.test1"}, fileAuthor) + _, err = fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.test1", Bucket: bucket}, fileAuthor) patch2.Reset() c.So(err, c.ShouldNotBeNil) @@ -1161,7 +1231,7 @@ func testDelete(t *testing.T) { }) c.Convey("test delete file success", t, func() { - rsp, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.test1"}, fileAuthor) + rsp, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: fileAuthor + "/test/upload.parts.test1", Bucket: bucket}, fileAuthor) c.So(err, c.ShouldBeNil) c.So(rsp, c.ShouldNotBeNil) _, err = os.Stat(filepath.Join(cnf.GetUploadDir(), fileAuthor, "test", "upload.parts.test1")) @@ -1172,6 +1242,7 @@ func testDelete(t *testing.T) { }) } + func testUploadVersionCommitErr(t *testing.T) { fileBiz := newFileBiz() c.Convey("test upload version commit error", t, func() { @@ -1213,8 +1284,7 @@ func testUploadVersionCommitErr(t *testing.T) { }, } fileAuthor2 := fmt.Sprintf("tester-fail-%s", time.Now().Format("20060102150405")) - for index, caseV := range cases { - t.Logf("test case %d", index) + for _, caseV := range cases { patch := gomonkey.ApplyFuncReturn(caseV.patch, caseV.output...) defer patch.Reset() _, err := fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ @@ -1223,10 +1293,12 @@ func testUploadVersionCommitErr(t *testing.T) { ContentType: tmp2.contentType, UseVersion: true, Sha256: tmp2.sha256, + Bucket: bucket, }, fileAuthor2) + patch.Reset() + c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, caseV.err.Error()) - patch.Reset() } patch := gomonkey.ApplyFuncReturn(git.PlainInit, nil, git.ErrRepositoryAlreadyExists) @@ -1238,24 +1310,26 @@ func testUploadVersionCommitErr(t *testing.T) { ContentType: tmp2.contentType, UseVersion: true, Sha256: tmp2.sha256, + Bucket: bucket, } _, err = fileBiz.Upload(context.TODO(), f, fileAuthor2) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "open error") patch.Reset() - patch2 := gomonkey.ApplyFuncReturn((*git.Repository).CommitObject, nil, git.ErrEmptyCommit) - patch2 = patch2.ApplyFuncReturn((*git.Repository).Head, nil, fmt.Errorf("head error")) + patch2 := gomonkey.ApplyFuncReturn((*git.Worktree).Commit, nil, git.ErrEmptyCommit) + patch2 = patch2.ApplyFuncSeq((*git.Repository).Head, []gomonkey.OutputCell{{Times: 2, Values: []interface{}{plumbing.NewHashReference(plumbing.ReferenceName("test"), plumbing.NewHash("xxxxxxxxxx")), nil}}, {Values: []interface{}{nil, fmt.Errorf("get head ref error")}}}) defer patch2.Reset() _, err = fileBiz.Upload(context.TODO(), f, fileAuthor2) c.So(err, c.ShouldNotBeNil) - c.So(err.Error(), c.ShouldContainSubstring, "head error") + c.So(err.Error(), c.ShouldContainSubstring, "get head ref error") t.Logf("test upload version commit error end,%s", err.Error()) patch2.Reset() }) } func TestFile(t *testing.T) { + t.Run("test make bucket", testMkBucket) t.Run("test upload", testPutFile) t.Run("test upload version commit error", testUploadVersionCommitErr) t.Run("test download", testDownload) @@ -1266,6 +1340,7 @@ func TestFile(t *testing.T) { t.Run("test version reader", testVersionReader) t.Run("test abort parts file", testAbortMultipartUpload) t.Run("test download range parts file", testDownloadRange) + t.Run("test list file", testList) t.Run("test delete file", testDelete) } diff --git a/internal/biz/file/minio.go b/internal/biz/file/minio.go new file mode 100644 index 0000000..2675701 --- /dev/null +++ b/internal/biz/file/minio.go @@ -0,0 +1,208 @@ +package file + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "fmt" + "io" + "os" + "path/filepath" + + "github.com/begonia-org/begonia/internal/pkg" + gosdk "github.com/begonia-org/go-sdk" + api "github.com/begonia-org/go-sdk/api/file/v1" + common "github.com/begonia-org/go-sdk/common/api/v1" + "github.com/minio/minio-go/v7" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type MinioUseCase struct { + minioClient *minio.Client + localFile *FileUsecaseImpl +} + +func NewMinioUseCase(minioClient *minio.Client, localFile *FileUsecaseImpl) FileUsecase { + return &MinioUseCase{minioClient: minioClient, localFile: localFile} +} +func (m *MinioUseCase) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { + err := m.minioClient.MakeBucket(ctx, in.Bucket, minio.MakeBucketOptions{Region: in.Region, ObjectLocking: in.ObjectLocking}) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to make bucket: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "make_bucket") + } + return &api.MakeBucketResponse{}, nil +} +func (m *MinioUseCase) Upload(ctx context.Context, in *api.UploadFileRequest, authorId string) (*api.UploadFileResponse, error) { + found, err := m.minioClient.BucketExists(ctx, in.Bucket) + if err != nil || !found { + return nil, gosdk.NewError(fmt.Errorf("failed to check bucket: %w or bucket %s not exist", err, in.Bucket), int32(common.Code_INTERNAL_ERROR), codes.Internal, "check_bucket") + } + contentLength := len(in.Content) + if in.UseVersion { + _ = m.minioClient.EnableVersioning(ctx, in.Bucket) + } + hash := sha256.New() + hash.Write([]byte(in.Content)) + checksum := hash.Sum(nil) + // checksumHex := hex.EncodeToString(checksum) + + // 设置对象元数据,包括 SHA-256 校验和 + userMetadata := map[string]string{ + "x-amz-checksum-algorithm": "SHA256", + "x-amz-checksum-sha256": base64.StdEncoding.EncodeToString(checksum), + } + info, err := m.minioClient.PutObject(ctx, in.Bucket, in.Key, bytes.NewReader(in.Content), int64(contentLength), minio.PutObjectOptions{ContentType: in.ContentType, DisableContentSha256: false, UserMetadata: userMetadata}) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to upload object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upload_object") + + } + err = m.localFile.repo.UpsertFile(ctx, &api.Files{ + Uid: m.localFile.snowflake.GenerateIDString(), + Engine: api.FileEngine_name[int32(*api.FileEngine_FILE_ENGINE_MINIO.Enum())], + Bucket: in.Bucket, + Key: in.Key, + IsDeleted: false, + Owner: authorId, + CreatedAt: timestamppb.Now(), + UpdatedAt: timestamppb.Now(), + }) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to upsert file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upsert_file") + + } + // log.Printf("upload object:%s,%s", info.Key, info.ChecksumSHA256) + return &api.UploadFileResponse{Uri: info.Key, Version: info.VersionID}, err +} +func (m *MinioUseCase) InitiateUploadFile(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { + return m.localFile.InitiateUploadFile(ctx, in) +} +func (m *MinioUseCase) UploadMultipartFileFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) { + return m.localFile.UploadMultipartFileFile(ctx, in) +} +func (m *MinioUseCase) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { + return m.localFile.AbortMultipartUpload(ctx, in) +} +func (m *MinioUseCase) CompleteMultipartUploadFile(ctx context.Context, in *api.CompleteMultipartUploadRequest, authorId string) (*api.CompleteMultipartUploadResponse, error) { + _, _ = m.localFile.MakeBucket(ctx, &api.MakeBucketRequest{Bucket: in.Bucket}) + originKey := in.Key + + rsp, err := m.localFile.CompleteMultipartUploadFile(ctx, in, authorId) + if err != nil { + return nil, err + } + filePath := filepath.Join(m.localFile.config.GetUploadDir(), in.Bucket, rsp.Uri) + file, err := os.Open(filePath) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to open file: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "open_file") + + } + defer file.Close() + stat, _ := os.Stat(filePath) + if in.UseVersion { + _ = m.minioClient.EnableVersioning(ctx, in.Bucket) + } + + userMetadata := map[string]string{ + "x-amz-meta-sha256": in.Sha256, + } + info, err := m.minioClient.PutObject(ctx, in.Bucket, originKey, file, stat.Size(), minio.PutObjectOptions{ContentType: in.ContentType, DisableContentSha256: false, PartSize: 1024 * 1024 * 8, ConcurrentStreamParts: true, UserMetadata: userMetadata}) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to upload object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "upload_object") + } + defer os.Remove(filePath) + + return &api.CompleteMultipartUploadResponse{Uri: info.Key, Version: info.VersionID}, nil +} +func (m *MinioUseCase) DownloadForRange(ctx context.Context, in *api.DownloadRequest, start int64, end int64, authorId string) ([]byte, int64, error) { + object, err := m.minioClient.GetObject(ctx, in.Bucket, in.Key, minio.GetObjectOptions{VersionID: in.Version, Checksum: true}) + if err != nil { + return nil, 0, gosdk.NewError(fmt.Errorf("failed to get %s %s object: %w", in.Bucket, in.Key, err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object") + } + if start > end && end > 0 { + err := gosdk.NewError(fmt.Errorf("%w:start=%d,end=%d", pkg.ErrInvalidRange, start, end), int32(api.FileSvrStatus_FILE_INVALIDATE_RANGE_ERR), codes.InvalidArgument, "invalid_range") + return nil, 0, err + + } + stat, err := object.Stat() + if err != nil { + return nil, 0, gosdk.NewError(fmt.Errorf("failed to get %s/%s object stat: %w", in.Bucket, in.Key, err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object_stat") + + } + var buf []byte + if end > 0 { + buf = make([]byte, end-start+1) + } else { + buf = make([]byte, stat.Size-start) + } + _, err = object.ReadAt(buf, start) + if err != nil && err != io.EOF { + return nil, 0, gosdk.NewError(fmt.Errorf("failed to read object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "read_object") + + } + return buf, stat.Size, nil + +} +func (m *MinioUseCase) Metadata(ctx context.Context, in *api.FileMetadataRequest, authorId string) (*api.FileMetadataResponse, error) { + + opt := minio.StatObjectOptions{VersionID: in.Version, Checksum: true} + + info, err := m.minioClient.StatObject(ctx, in.Bucket, in.Key, opt) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to get object stat: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object_stat") + } + + checksum := info.ChecksumSHA256 + if checksum != "" { + c, _ := base64.StdEncoding.DecodeString(checksum) + checksum = hex.EncodeToString(c) + } + if checksum == "" { + checksum = info.UserMetadata["Sha256"] + } + return &api.FileMetadataResponse{Size: info.Size, + ContentType: info.ContentType, + Version: info.VersionID, + ModifyTime: int64(info.LastModified.Second()), + Etag: info.ETag, + Name: info.Key, + Sha256: checksum, + }, nil +} +func (m *MinioUseCase) Version(ctx context.Context, bucket, key, authorId string) (string, error) { + info, err := m.minioClient.StatObject(ctx, bucket, key, minio.StatObjectOptions{Checksum: true}) + if err != nil { + return "", gosdk.NewError(fmt.Errorf("failed to get object stat: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object_stat") + } + return info.VersionID, nil +} +func (m *MinioUseCase) Download(ctx context.Context, in *api.DownloadRequest, authorId string) ([]byte, error) { + object, err := m.minioClient.GetObject(ctx, in.Bucket, in.Key, minio.GetObjectOptions{VersionID: in.Version}) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to get object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object") + } + stat, err := object.Stat() + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to get object stat: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "get_object_stat") + + } + buf := make([]byte, stat.Size) + _, err = object.Read(buf) + if err != nil && err != io.EOF { + return nil, gosdk.NewError(fmt.Errorf("failed to read object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "read_object") + + } + return buf, nil +} +func (m *MinioUseCase) Delete(ctx context.Context, in *api.DeleteRequest, authorId string) (*api.DeleteResponse, error) { + err := m.minioClient.RemoveObject(ctx, in.Bucket, in.Key, minio.RemoveObjectOptions{VersionID: in.Version}) + if err != nil { + return nil, gosdk.NewError(fmt.Errorf("failed to remove object: %w", err), int32(common.Code_INTERNAL_ERROR), codes.Internal, "remove_object") + } + return &api.DeleteResponse{}, nil +} +func (m *MinioUseCase) List(ctx context.Context, in *api.ListFilesRequest, authorId string) ([]*api.Files, error) { + return m.localFile.List(ctx, in, authorId) +} diff --git a/internal/biz/file/minio_test.go b/internal/biz/file/minio_test.go new file mode 100644 index 0000000..ac48db0 --- /dev/null +++ b/internal/biz/file/minio_test.go @@ -0,0 +1,446 @@ +package file_test + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "math" + "os" + "testing" + "time" + + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/biz/file" + "github.com/begonia-org/begonia/internal/data" + "github.com/begonia-org/begonia/internal/pkg" + cfg "github.com/begonia-org/begonia/internal/pkg/config" + api "github.com/begonia-org/go-sdk/api/file/v1" + "github.com/minio/minio-go/v7" + c "github.com/smartystreets/goconvey/convey" + "github.com/spark-lence/tiga" +) + +var minioBucket = "" +var minioFileAuthor = "" +var sha256Str = "" +var minioUploadId = "" +var minioBigFileSha256 = "" + +func newFileMinioBiz() file.FileUsecase { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + conf := cfg.NewConfig(cnf) + repo := data.NewFileRepo(cnf, gateway.Log) + + engines := file.NewFileUsecase(conf, repo) + return engines[api.FileEngine_FILE_ENGINE_MINIO.String()] +} +func testMinioMkBucket(t *testing.T) { + fileBiz := newFileMinioBiz() + snk, _ := tiga.NewSnowflake(1) + + minioFileAuthor = snk.GenerateIDString() + minioBucket = fmt.Sprintf("bucket-minio-biz-%s", time.Now().Format("20060102150405")) + c.Convey("test make bucket success", t, func() { + rsp, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ + Bucket: minioBucket, + }) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + }) + c.Convey("test make bucket fail", t, func() { + patch := gomonkey.ApplyFuncReturn((*minio.Client).MakeBucket, fmt.Errorf("mkdir error")) + defer patch.Reset() + _, err := fileBiz.MakeBucket(context.TODO(), &api.MakeBucketRequest{ + Bucket: "", + }) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "mkdir error") + }) +} + +func testMinioUpload(t *testing.T) { + fileBiz := newFileMinioBiz() + + c.Convey("test upload file success", t, func() { + rsp, err := fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ + Bucket: minioBucket, + Key: "test.txt", + Content: []byte("hello"), + UseVersion: true, + }, minioFileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + shaer := sha256.New() + shaer.Write([]byte("hello")) + sha256Str = hex.EncodeToString(shaer.Sum(nil)) + }) + c.Convey("test upload file fail", t, func() { + patch := gomonkey.ApplyFuncReturn((*minio.Client).PutObject, nil, fmt.Errorf("upload error")) + defer patch.Reset() + _, err := fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ + Bucket: minioBucket, + Key: "test.txt", + Content: []byte("hello"), + }, minioFileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "upload error") + _, err = fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ + Bucket: "-13234sdddfe", + Key: "test.txt", + Content: []byte("hello"), + }, minioFileAuthor) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "not exist") + + patch2 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.Upsert, fmt.Errorf("upsert error")) + defer patch2.Reset() + _, err = fileBiz.Upload(context.TODO(), &api.UploadFileRequest{ + Bucket: minioBucket, + Key: "test2.txt", + Content: []byte("hello2"), + }, minioFileAuthor) + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "failed to upsert file") + + }) +} + +func testMinioDownload(t *testing.T) { + fileBiz := newFileMinioBiz() + + c.Convey("test download file success", t, func() { + _, err := fileBiz.Download(context.TODO(), &api.DownloadRequest{ + Bucket: minioBucket, + Key: "test.txt", + }, minioFileAuthor) + c.So(err, c.ShouldBeNil) + }) + c.Convey("test download file fail", t, func() { + patch := gomonkey.ApplyFuncReturn((*minio.Client).GetObject, nil, fmt.Errorf("download error")) + defer patch.Reset() + _, err := fileBiz.Download(context.TODO(), &api.DownloadRequest{ + Bucket: minioBucket, + Key: "test.txt", + }, minioFileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "download error") + + patch2 := gomonkey.ApplyFuncReturn((*minio.Object).Read, 0, fmt.Errorf("read error")) + defer patch2.Reset() + _, err = fileBiz.Download(context.TODO(), &api.DownloadRequest{ + Bucket: minioBucket, + Key: "test.txt", + }, minioFileAuthor) + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "read error") + + patch3 := gomonkey.ApplyFuncReturn((*minio.Object).Stat, nil, fmt.Errorf("stat error")) + defer patch3.Reset() + _, err = fileBiz.Download(context.TODO(), &api.DownloadRequest{ + Bucket: minioBucket, + Key: "test.txt", + }, minioFileAuthor) + patch3.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "stat error") + }) +} +func testMinioMeta(t *testing.T) { + fileBiz := newFileMinioBiz() + c.Convey("test get meta success", t, func() { + rsp, err := fileBiz.Metadata(context.TODO(), &api.FileMetadataRequest{ + Bucket: minioBucket, + Key: "test.txt", + Engine: api.FileEngine_FILE_ENGINE_MINIO.String(), + }, minioFileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + c.So(rsp.Sha256, c.ShouldEqual, sha256Str) + c.So(rsp.Version, c.ShouldNotBeEmpty) + }) + c.Convey("test get meta fail", t, func() { + patch := gomonkey.ApplyFuncReturn((*minio.Client).StatObject, nil, fmt.Errorf("get meta error")) + defer patch.Reset() + _, err := fileBiz.Metadata(context.TODO(), &api.FileMetadataRequest{ + Bucket: minioBucket, + Key: "test.txt", + }, minioFileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get meta error") + }) + +} +func testMinioVersion(t *testing.T) { + fileBiz := newFileMinioBiz() + c.Convey("test get version success", t, func() { + rsp, err := fileBiz.Version(context.TODO(), minioBucket, "test.txt", minioFileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeEmpty) + }) + c.Convey("test get version fail", t, func() { + patch := gomonkey.ApplyFuncReturn((*minio.Client).StatObject, nil, fmt.Errorf("get version error")) + defer patch.Reset() + _, err := fileBiz.Version(context.TODO(), minioBucket, "test.txt", minioFileAuthor) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get version error") + }) +} +func testMinioInitPartsUpload(t *testing.T) { + fileBiz := newFileMinioBiz() + c.Convey("test init parts upload success", t, func() { + rsp, err := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ + Key: "test-minio.txt", + }) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + minioUploadId = rsp.UploadId + }) + +} +func testMinioUploadParts(t *testing.T) { + c.Convey("test upload parts success", t, func() { + + bigTmpFile, err := generateRandomFile(1024 * 1024 * 32) + if err != nil { + t.Error(err) + } + err = uploadParts(bigTmpFile.path, minioUploadId, "test-minio.txt", t) + c.So(err, c.ShouldBeNil) + minioBigFileSha256 = bigTmpFile.sha256 + }) +} +func testMinioCompleteMultipartUploadFile(t *testing.T) { + fileBiz := newFileMinioBiz() + + c.Convey("test complete upload fail", t, func() { + rsp, _ := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ + Key: "test-minio-upload-fail.txt", + }) + uploadId := rsp.UploadId + bigTmpFile, err := generateRandomFile(1024 * 1024 * 32) + if err != nil { + t.Error(err) + } + _ = uploadParts(bigTmpFile.path, uploadId, "test-minio-upload-fail.txt", t) + cases := []struct { + patch interface{} + output []interface{} + exceptErr error + }{ + { + patch: (*file.FileUsecaseImpl).CompleteMultipartUploadFile, + output: []interface{}{nil, fmt.Errorf("complete error")}, + exceptErr: fmt.Errorf("complete error"), + }, + { + patch: os.Open, + output: []interface{}{nil, fmt.Errorf("open error")}, + exceptErr: fmt.Errorf("open error"), + }, + { + patch: (*minio.Client).PutObject, + output: []interface{}{nil, fmt.Errorf("put error")}, + exceptErr: fmt.Errorf("put error"), + }, + } + for _, v := range cases { + patchx := gomonkey.ApplyFuncReturn(v.patch, v.output...) + defer patchx.Reset() + _, err := fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ + Key: "test-minio.txt", + UploadId: uploadId, + Bucket: minioBucket, + ContentType: "text/plain", + UseVersion: true, + Sha256: minioBigFileSha256, + }, minioUploadId) + patchx.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, v.exceptErr.Error()) + + } + _, err = fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ + Key: "test-minio.txt", + UploadId: uploadId, + Bucket: "", + ContentType: "text/plain", + UseVersion: true, + Sha256: minioBigFileSha256, + }, minioUploadId) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrBucketNotFound.Error()) + t.Logf("test complete upload fail finsih") + }) + c.Convey("test complete upload success", t, func() { + rsp, err := fileBiz.CompleteMultipartUploadFile(context.TODO(), &api.CompleteMultipartUploadRequest{ + Key: "test-minio.txt", + UploadId: minioUploadId, + Bucket: minioBucket, + ContentType: "text/plain", + UseVersion: true, + Sha256: minioBigFileSha256, + }, minioUploadId) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + }) +} +func testMinioDownloadForRange(t *testing.T) { + fileBiz := newFileMinioBiz() + fileSize := 1024 * 1024 * 32 + c.Convey("test download file success", t, func() { + partSize := int64(2 * 1024 * 1024) + partCount := math.Ceil(float64(fileSize) / float64(partSize)) + shaer := sha256.New() + for i := 0; i < int(partCount); i++ { + rangeStartAt := int64(i) * partSize + rangeEndAt := rangeStartAt + partSize - 1 + if rangeEndAt > int64(fileSize) { + rangeEndAt = int64(fileSize) - 1 + } + data, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ + Key: "test-minio.txt", + Bucket: minioBucket, + }, rangeStartAt, rangeEndAt, minioFileAuthor) + c.So(err, c.ShouldBeNil) + shaer.Write(data) + } + b := shaer.Sum(nil) + c.So(hex.EncodeToString(b), c.ShouldEqual, minioBigFileSha256) + + meta, err := fileBiz.Metadata(context.Background(), &api.FileMetadataRequest{Key: "test-minio.txt", Bucket: minioBucket, Engine: api.FileEngine_FILE_ENGINE_MINIO.String()}, minioFileAuthor) + c.So(err, c.ShouldBeNil) + c.So(meta.Sha256, c.ShouldEqual, minioBigFileSha256) + }) + c.Convey("test download file fail", t, func() { + _, _, err := fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ + Key: "test-minio-2.txt", + Bucket: minioBucket, + }, 0, 1024, minioFileAuthor) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "not exist") + + _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ + Key: "test-minio.txt", + Bucket: minioBucket, + }, 1024, 1, minioUploadId) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrInvalidRange.Error()) + + _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ + Key: "test-minio.txt", + Bucket: minioBucket, + }, 1024, 0, minioFileAuthor) + c.So(err, c.ShouldBeNil) + + patch := gomonkey.ApplyFuncReturn((*minio.Client).GetObject, nil, fmt.Errorf("get object error")) + defer patch.Reset() + _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ + Key: "test-minio.txt", + Bucket: minioBucket, + }, 1024, 0, minioFileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "get object error") + + patch2 := gomonkey.ApplyFuncReturn((*minio.Object).ReadAt, 0, fmt.Errorf("read error")) + defer patch2.Reset() + _, _, err = fileBiz.DownloadForRange(context.Background(), &api.DownloadRequest{ + Key: "test-minio.txt", + Bucket: minioBucket, + }, 1024, 0, minioFileAuthor) + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "read error") + }) + +} +func testMinioAbortMultipartUpload(t *testing.T) { + fileBiz := newFileMinioBiz() + c.Convey("test abort upload success", t, func() { + rsp, err := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ + Key: "test-minio-2.txt", + }) + _, _ = fileBiz.UploadMultipartFileFile(context.TODO(), &api.UploadMultipartFileRequest{ + UploadId: rsp.UploadId, + Key: "test-minio-2.txt", + PartNumber: 1, + Content: []byte("hello"), + }) + c.So(err, c.ShouldBeNil) + _, err = fileBiz.AbortMultipartUpload(context.TODO(), &api.AbortMultipartUploadRequest{ + UploadId: rsp.UploadId, + }) + c.So(err, c.ShouldBeNil) + }) + c.Convey("test abort upload fail", t, func() { + rsp, _ := fileBiz.InitiateUploadFile(context.TODO(), &api.InitiateMultipartUploadRequest{ + Key: "test-minio-2.txt", + }) + patch := gomonkey.ApplyFuncReturn(os.RemoveAll, fmt.Errorf("abort remove all error")) + defer patch.Reset() + _, err := fileBiz.AbortMultipartUpload(context.TODO(), &api.AbortMultipartUploadRequest{ + UploadId: rsp.UploadId, + }) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "abort remove all error") + }) + +} +func testMinioDelete(t *testing.T) { + fileBiz := newFileMinioBiz() + c.Convey("test delete file success", t, func() { + _, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "test-minio.txt", Bucket: minioBucket}, minioFileAuthor) + c.So(err, c.ShouldBeNil) + }) + c.Convey("test delete file fail", t, func() { + patch := gomonkey.ApplyFuncReturn((*minio.Client).RemoveObject, fmt.Errorf("delete error")) + defer patch.Reset() + _, err := fileBiz.Delete(context.Background(), &api.DeleteRequest{Key: "test-minio.txt", Bucket: minioBucket}, minioFileAuthor) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "delete error") + }) +} +func testMinioList(t *testing.T) { + fileBiz := newFileMinioBiz() + c.Convey("test list success", t, func() { + rsp, err := fileBiz.List(context.Background(), &api.ListFilesRequest{Bucket: minioBucket, Page: 1, PageSize: 20}, minioFileAuthor) + c.So(err, c.ShouldBeNil) + c.So(rsp, c.ShouldNotBeNil) + c.So(len(rsp), c.ShouldEqual, 1) + }) + c.Convey("test list fail", t, func() { + _, err := fileBiz.List(context.Background(), &api.ListFilesRequest{Bucket: minioBucket, Page: -1, PageSize: -1}, minioFileAuthor) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "SQL syntax") + }) + +} +func TestMinio(t *testing.T) { + t.Run("testMinioMkBucket", testMinioMkBucket) + t.Run("testMinioUpload", testMinioUpload) + t.Run("testMinioDownload", testMinioDownload) + t.Run("testMinioMeta", testMinioMeta) + t.Run("testMinioVersion", testMinioVersion) + t.Run("testMinioInitPartsUpload", testMinioInitPartsUpload) + t.Run("testMinioUploadParts", testMinioUploadParts) + t.Run("testMinioCompleteMultipartUploadFile", testMinioCompleteMultipartUploadFile) + t.Run("testMinioDownloadForRange", testMinioDownloadForRange) + t.Run("testMinioList", testMinioList) + t.Run("testMinioAbortMultipartUpload", testMinioAbortMultipartUpload) + t.Run("testMinioDelete", testMinioDelete) +} diff --git a/internal/data/app_test.go b/internal/data/app_test.go index cebc8ed..cbc9c9e 100644 --- a/internal/data/app_test.go +++ b/internal/data/app_test.go @@ -252,7 +252,7 @@ func testListApp(t *testing.T) { env = begonia.Env } repo := NewAppRepo(cfg.ReadConfig(env), gateway.Log) - snk, _ := tiga.NewSnowflake(1) + snk, _ := tiga.NewSnowflake(5) rand := rand.New(rand.NewSource(time.Now().UnixNano())) status := []api.APPStatus{api.APPStatus_APP_ENABLED, api.APPStatus_APP_DISABLED} tags := [3]string{"tags-1", "tags-3", "tags-2"} diff --git a/internal/data/data.go b/internal/data/data.go index 2a614cd..083322c 100644 --- a/internal/data/data.go +++ b/internal/data/data.go @@ -52,6 +52,7 @@ func NewEtcd(config *tiga.Configuration) *tiga.EtcdDao { var ProviderSet = wire.NewSet(NewMySQL, NewRDB, NewEtcd, + NewFileRepoImpl, GetRDBClient, NewData, NewCurdImpl, diff --git a/internal/data/file.go b/internal/data/file.go index 632d637..4f2d5d4 100644 --- a/internal/data/file.go +++ b/internal/data/file.go @@ -1,42 +1,70 @@ package data -// import ( -// "context" +import ( + "context" + "fmt" -// "github.com/begonia-org/begonia/internal/biz/file" -// common "github.com/begonia-org/go-sdk/common/api/v1" -// ) + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/biz/file" + api "github.com/begonia-org/go-sdk/api/file/v1" + "google.golang.org/protobuf/types/known/timestamppb" -// type fileRepoImpl struct { -// data *Data -// } + "github.com/spark-lence/tiga" +) -// func NewFileRepoImpl(data *Data) file.FileRepo { -// return &fileRepoImpl{data: data} -// } +type fileRepoImpl struct { + data *Data + curd biz.CURD +} -// // mysql -// // AddFile(ctx context.Context, files []*common.Files) error -// // DeleteFile(ctx context.Context, files []*common.Files) error -// // UpdateFile(ctx context.Context, files []*common.Files) error -// // GetFile(ctx context.Context, uri string) (*common.Files, error) -// // ListFile(ctx context.Context, name []string) ([]*common.Files, error) -// func (r *fileRepoImpl) UploadFile(ctx context.Context, files []*common.Files) error { -// return nil -// } +func NewFileRepoImpl(data *Data, curd biz.CURD) file.FileRepo { + return &fileRepoImpl{data: data, curd: curd} +} -// func (r *fileRepoImpl) DeleteFile(ctx context.Context, files []*common.Files) error { -// return nil -// } +func (f *fileRepoImpl) UpsertFile(ctx context.Context, in *api.Files) error { + in.UpdatedAt = timestamppb.Now() + mask := make([]string, 0) + if in.UpdateMask != nil { + mask = in.UpdateMask.Paths + } + return f.data.db.Upsert(ctx, in, mask...) +} +func (f *fileRepoImpl) DelFile(ctx context.Context, engine, bucket, key string) error { + // return f.curd.Del(ctx, &api.Files{Uid: fid},false) + return f.data.db.UpdateSelectColumns(ctx, &api.Files{Engine: engine, Bucket: bucket, Key: key}, &api.Files{IsDeleted: true}, "is_deleted") +} +func (f *fileRepoImpl) UpsertBucket(ctx context.Context, bucket *api.Buckets) error { + bucket.UpdatedAt = timestamppb.Now() + mask := make([]string, 0) + if bucket.UpdateMask != nil { + mask = bucket.UpdateMask.Paths + } + return f.data.db.Upsert(ctx, bucket, mask...) +} +func (f *fileRepoImpl) DelBucket(ctx context.Context, bucketId string) error { + return f.curd.Del(ctx, &api.Buckets{Uid: bucketId}, false) +} +func (f *fileRepoImpl) List(ctx context.Context, page, pageSize int32, bucket, engine, owner string) ([]*api.Files, error) { + files := make([]*api.Files, 0) + pagination := &tiga.Pagination{ + Page: page, + PageSize: pageSize, + Args: make([]interface{}, 0), + } + pagination.Query = "owner = ?" + pagination.Args = append(pagination.Args, owner) + if bucket != "" { + pagination.Query = fmt.Sprintf("%s and bucket = ?", pagination.Query) + pagination.Args = append(pagination.Args, bucket) + } + if engine != "" { + pagination.Query = fmt.Sprintf("%s and engine = ?", pagination.Query) + pagination.Args = append(pagination.Args, engine) + } + err := f.curd.List(ctx, &files, pagination) -// func (r *fileRepoImpl) UpdateFile(ctx context.Context, files []*common.Files) error { -// return nil -// } - -// func (r *fileRepoImpl) GetFile(ctx context.Context, uri string) (*common.Files, error) { -// return nil, nil -// } - -// func (r *fileRepoImpl) ListFile(ctx context.Context, name []string) ([]*common.Files, error) { -// return nil, nil -// } + if err != nil { + return nil, err + } + return files, nil +} diff --git a/internal/data/file_test.go b/internal/data/file_test.go new file mode 100644 index 0000000..fe23d10 --- /dev/null +++ b/internal/data/file_test.go @@ -0,0 +1,134 @@ +package data + +import ( + "context" + "fmt" + "testing" + + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + cfg "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + api "github.com/begonia-org/go-sdk/api/file/v1" + c "github.com/smartystreets/goconvey/convey" + "github.com/spark-lence/tiga" + "google.golang.org/protobuf/types/known/fieldmaskpb" + "google.golang.org/protobuf/types/known/timestamppb" +) + +var fileBucketId = "" + +func testUpsertFile(t *testing.T) { + c.Convey("Test UpsertFile", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + snk, _ := tiga.NewSnowflake(1) + file := &api.Files{ + Engine: "test", + Bucket: "test", + Key: "test", + Uid: snk.GenerateIDString(), + Owner: "test", + CreatedAt: timestamppb.Now(), + UpdatedAt: timestamppb.Now(), + UpdateMask: &fieldmaskpb.FieldMask{}, + } + err := f.UpsertFile(context.Background(), file) + c.So(err, c.ShouldBeNil) + + file.UpdateMask.Paths = append(file.UpdateMask.Paths, "updated_at") + err = f.UpsertFile(context.Background(), file) + + c.So(err, c.ShouldBeNil) + }) +} + +func testUpsertBucket(t *testing.T) { + c.Convey("Test UpsertBucket", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + snk, _ := tiga.NewSnowflake(1) + bucket := &api.Buckets{ + Engine: "test", + Bucket: "test", + Uid: snk.GenerateIDString(), + Owner: "test", + CreatedAt: timestamppb.Now(), + UpdatedAt: timestamppb.Now(), + UpdateMask: &fieldmaskpb.FieldMask{}, + } + fileBucketId = bucket.Uid + err := f.UpsertBucket(context.Background(), bucket) + c.So(err, c.ShouldBeNil) + bucket.UpdateMask.Paths = append(bucket.UpdateMask.Paths, "updated_at") + err = f.UpsertBucket(context.Background(), bucket) + c.So(err, c.ShouldBeNil) + }) +} +func testFileList(t *testing.T) { + c.Convey("Test file List", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + files, err := f.List(context.Background(), 1, 10, "test", "test", "test") + c.So(err, c.ShouldBeNil) + c.So(files, c.ShouldNotBeNil) + }) + c.Convey("Test file List fail", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.Pagination, fmt.Errorf("page error")) + defer patch.Reset() + + files, err := f.List(context.Background(), 1, 10, "", "", "test") + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "page error") + c.So(files, c.ShouldBeEmpty) + }) +} +func testDeleteFile(t *testing.T) { + c.Convey("Test DeleteFile", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + err := f.DelFile(context.Background(), "test", "test", "test") + c.So(err, c.ShouldBeNil) + }) +} +func testDeleteBucket(t *testing.T) { + c.Convey("Test DeleteBucket", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := cfg.ReadConfig(env) + f := NewFileRepo(conf, gateway.Log) + err := f.DelBucket(context.Background(), fileBucketId) + c.So(err, c.ShouldBeNil) + }) +} +func TestFile(t *testing.T) { + t.Run("TestUpsertFile", testUpsertFile) + t.Run("TestUpsertBucket", testUpsertBucket) + t.Run("TestFileList", testFileList) + t.Run("TestDeleteFile", testDeleteFile) + t.Run("TestDeleteBucket", testDeleteBucket) +} diff --git a/internal/data/wire.go b/internal/data/wire.go index 321839f..7ae4055 100644 --- a/internal/data/wire.go +++ b/internal/data/wire.go @@ -7,6 +7,8 @@ import ( "time" "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/biz/file" + "github.com/begonia-org/begonia/internal/biz/endpoint" "github.com/begonia-org/begonia/internal/pkg/config" "github.com/begonia-org/go-sdk/logger" @@ -42,3 +44,6 @@ func NewDataRepo(cfg *tiga.Configuration, log logger.Logger) *Data { func NewLocker(cfg *tiga.Configuration, log logger.Logger, key string, ttl time.Duration, retry int) biz.DataLock { panic(wire.Build(ProviderSet)) } +func NewFileRepo(cfg *tiga.Configuration, log logger.Logger) file.FileRepo { + panic(wire.Build(ProviderSet, config.NewConfig)) +} diff --git a/internal/data/wire_gen.go b/internal/data/wire_gen.go index 29f60d1..63a45f2 100644 --- a/internal/data/wire_gen.go +++ b/internal/data/wire_gen.go @@ -9,6 +9,7 @@ package data import ( "github.com/begonia-org/begonia/internal/biz" "github.com/begonia-org/begonia/internal/biz/endpoint" + "github.com/begonia-org/begonia/internal/biz/file" "github.com/begonia-org/begonia/internal/pkg/config" "github.com/begonia-org/go-sdk/logger" "github.com/spark-lence/tiga" @@ -93,3 +94,14 @@ func NewLocker(cfg *tiga.Configuration, log logger.Logger, key string, ttl time. bizDataLock := NewDataLock(client, key, ttl, retry) return bizDataLock } + +func NewFileRepo(cfg *tiga.Configuration, log logger.Logger) file.FileRepo { + mySQLDao := NewMySQL(cfg) + redisDao := NewRDB(cfg) + etcdDao := NewEtcd(cfg) + data := NewData(mySQLDao, redisDao, etcdDao) + configConfig := config.NewConfig(cfg) + curd := NewCurdImpl(mySQLDao, configConfig) + fileRepo := NewFileRepoImpl(data, curd) + return fileRepo +} diff --git a/internal/migrate/app.go b/internal/migrate/app.go index ab73537..f63a88f 100644 --- a/internal/migrate/app.go +++ b/internal/migrate/app.go @@ -3,6 +3,7 @@ package migrate import ( "context" "encoding/json" + "fmt" "log" "os" "path/filepath" @@ -22,63 +23,57 @@ type APPOperator struct { func NewAPPOperator(mysql *tiga.MySQLDao) *APPOperator { return &APPOperator{mysql: mysql} } -func dumpInitApp(app *api.Apps) { +func dumpInitApp(app *api.Apps) error { log.Print("########################################admin-app###############################") log.Printf("Init appid:%s", app.Appid) log.Printf("Init accessKey:%s", app.AccessKey) log.Printf("Init secret:%s", app.Secret) homeDir, err := os.UserHomeDir() if err != nil { - log.Fatalf(err.Error()) - return + return err } path := filepath.Join(homeDir, ".begonia") if err := os.MkdirAll(path, os.ModePerm); err != nil { - log.Fatalf(err.Error()) - return + return err } file, err := os.Create(filepath.Join(path, "admin-app.json")) if err != nil { - log.Fatalf(err.Error()) - return + return err } defer file.Close() encoder := json.NewEncoder(file) if err := encoder.Encode(app); err != nil { - log.Fatalf(err.Error()) - return + return err } log.Printf("Init admin-app config file at :%s", file.Name()) log.Print("#################################################################################") - + return nil } -func (m *APPOperator) InitAdminAPP(owner string) error { +func (m *APPOperator) InitAdminAPP(owner string) (err error) { app := &api.Apps{} defer func() { if app.Appid != "" { - dumpInitApp(app) + if errInit := dumpInitApp(app); errInit != nil { + err = errInit + } } }() - err := m.mysql.First(context.TODO(), app, "name = ?", "admin-app") + err = m.mysql.First(context.TODO(), app, "name = ?", "admin-app") if err != nil && err != gorm.ErrRecordNotFound { - log.Fatalf("InitAdminAPP error:%v", err) return err } if app.Appid == "" { - snk, err := tiga.NewSnowflake(1) - if err != nil { - return err - } - // 初始化数据 - // uid := snk.GenerateID() - accessKey, err := biz.GenerateAppAccessKey() - if err != nil { + snk, errSnk := tiga.NewSnowflake(1) + if errSnk != nil { + err = errSnk return err } + accessKey, errAccess := biz.GenerateAppAccessKey() - secret, err := biz.GenerateAppSecret() + secret, errSecret := biz.GenerateAppSecret() - if err != nil { + if errAccess != nil || errSecret != nil { + err = fmt.Errorf("failed to generate accessKey or secret: %v,%v", errAccess, errSecret) return err } diff --git a/internal/migrate/migrate.go b/internal/migrate/migrate.go index 293e097..9978c9b 100644 --- a/internal/migrate/migrate.go +++ b/internal/migrate/migrate.go @@ -5,6 +5,7 @@ import ( app "github.com/begonia-org/go-sdk/api/app/v1" endpoint "github.com/begonia-org/go-sdk/api/endpoint/v1" + file "github.com/begonia-org/go-sdk/api/file/v1" api "github.com/begonia-org/go-sdk/api/user/v1" "github.com/google/wire" @@ -25,16 +26,13 @@ type MySQLMigrate struct { func NewTableModels() []TableModel { tables := make([]TableModel, 0) - tables = append(tables, api.Users{}, endpoint.Endpoints{}, app.Apps{}) + tables = append(tables, api.Users{}, endpoint.Endpoints{}, app.Apps{}, file.Files{}, file.Buckets{}) return tables } func NewMySQLMigrate(mysql *tiga.MySQLDao, models ...TableModel) *MySQLMigrate { mysql.RegisterTimeSerializer() return &MySQLMigrate{mysql: mysql, models: models} } -func (m *MySQLMigrate) BindModel(model interface{}) { - m.models = append(m.models, model) -} func (m *MySQLMigrate) Do() error { diff --git a/internal/migrate/operator_test.go b/internal/migrate/operator_test.go new file mode 100644 index 0000000..7fa53b6 --- /dev/null +++ b/internal/migrate/operator_test.go @@ -0,0 +1,149 @@ +package migrate_test + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/internal" + "github.com/begonia-org/begonia/internal/biz" + "github.com/begonia-org/begonia/internal/migrate" + c "github.com/smartystreets/goconvey/convey" + "github.com/spark-lence/tiga" +) + +func TestOperator(t *testing.T) { + c.Convey("TestOperator", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + + operator := internal.InitOperatorApp(cnf) + err := operator.Init() + c.So(err, c.ShouldBeNil) + + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, nil) + patch = patch.ApplyFuncReturn(tiga.MySQLDao.Create, nil) + defer patch.Reset() + operator = internal.InitOperatorApp(cnf) + err = operator.Init() + c.So(err, c.ShouldBeNil) + }) + c.Convey("TestOperator fail", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + + operator := internal.InitOperatorApp(cnf) + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.AutoMigrate, fmt.Errorf("migration failed")) + defer patch.Reset() + err := operator.Init() + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "migration failed") + + patch1 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("first failed")) + defer patch1.Reset() + operator = internal.InitOperatorApp(cnf) + err = operator.Init() + patch1.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "first failed") + + patch2 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, nil) + patch2 = patch2.ApplyFuncReturn(tiga.NewSnowflake, nil, fmt.Errorf("snowflake failed")) + defer patch2.Reset() + operator = internal.InitOperatorApp(cnf) + err = operator.Init() + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "snowflake failed") + + patch3 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, nil) + patch3 = patch3.ApplyFuncReturn(tiga.EncryptStructAES, fmt.Errorf("encrypt failed")) + defer patch3.Reset() + operator = internal.InitOperatorApp(cnf) + err = operator.Init() + patch3.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "encrypt failed") + }) +} + +func TestAppOperatorFail(t *testing.T) { + c.Convey("TestAppOperator fail", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + mysql := tiga.NewMySQLDao(cnf) + operator := migrate.NewAPPOperator(mysql) + cases := []struct { + patch interface{} + output []interface{} + err error + }{ + + { + patch: tiga.NewSnowflake, + output: []interface{}{nil, fmt.Errorf("snowflake failed")}, + err: fmt.Errorf("snowflake failed"), + }, + { + patch: biz.GenerateAppAccessKey, + output: []interface{}{"", fmt.Errorf("accessKey failed")}, + err: fmt.Errorf("accessKey failed"), + }, + { + patch: os.UserHomeDir, + output: []interface{}{"", fmt.Errorf("homeDir failed")}, + err: fmt.Errorf("homeDir failed"), + }, + { + patch: os.MkdirAll, + output: []interface{}{fmt.Errorf("mkdir failed")}, + err: fmt.Errorf("mkdir failed"), + }, + { + patch: os.Create, + output: []interface{}{nil, fmt.Errorf("create failed")}, + err: fmt.Errorf("create failed"), + }, + { + patch: (*json.Encoder).Encode, + output: []interface{}{fmt.Errorf("encode failed")}, + err: fmt.Errorf("encode failed"), + }, + } + patch := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, fmt.Errorf("first failed")) + defer patch.Reset() + err := operator.InitAdminAPP("test") + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "first failed") + + patch1 := gomonkey.ApplyFuncReturn(tiga.MySQLDao.First, nil) + patch1 = patch1.ApplyFuncReturn(tiga.MySQLDao.Create, fmt.Errorf("create failed")) + defer patch1.Reset() + for _, caseV := range cases { + patch2 := gomonkey.ApplyFuncReturn(caseV.patch, caseV.output...) + defer patch2.Reset() + err := operator.InitAdminAPP("test") + patch2.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, caseV.err.Error()) + + } + patch1.Reset() + + }) +} diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 90a0d1c..fc81697 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -6,6 +6,7 @@ import ( "strings" goloadbalancer "github.com/begonia-org/go-loadbalancer" + file "github.com/begonia-org/go-sdk/api/file/v1" "github.com/spark-lence/tiga" ) @@ -279,3 +280,15 @@ func (c *Config) GetRSAPubKey() string { return c.getWithEnv("auth.rsa.public_key") } +func (c *Config) GetFileEngines() ([]*file.FileSystemEngine, error) { + engines := make([]*file.FileSystemEngine, 0) + if err := c.UnmarshalKey(fmt.Sprintf("%s.file.engines", c.GetEnv()), &engines); err == nil && len(engines) > 0 { + return engines, nil + } + err := c.UnmarshalKey("file.engines", &engines) + if err != nil { + return nil, fmt.Errorf("get file engines failed:%w", err) + } + return engines, nil + +} diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index 7a71b6a..3057892 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -65,6 +65,8 @@ func TestConfig(t *testing.T) { c.So(config.GetPlugins(), c.ShouldNotBeEmpty) _, err := config.GetRPCPlugins() c.So(err, c.ShouldBeNil) + _, err = config.GetFileEngines() + c.So(err, c.ShouldBeNil) // c.So(len(ss), c.ShouldBeGreaterThan, 0) c.So(config.GetEndpointsPrefix(), c.ShouldNotBeEmpty) c.So(config.GetGatewayDescriptionOut(), c.ShouldNotBeEmpty) @@ -90,6 +92,10 @@ func TestConfig(t *testing.T) { ss, err := config.GetRPCPlugins() c.So(err, c.ShouldNotBeNil) c.So(ss, c.ShouldBeNil) + eng, err := config.GetFileEngines() + c.So(err, c.ShouldNotBeNil) + c.So(eng, c.ShouldBeNil) + patch.Reset() cnf = conf.ReadConfig("dev") config2 := cfg.NewConfig(cnf) c.So(config2.GetEndpointsPrefix(), c.ShouldNotBeEmpty) @@ -97,5 +103,12 @@ func TestConfig(t *testing.T) { c.So(config2.GetRSAPriKey(), c.ShouldNotBeEmpty) c.So(config2.GetRSAPubKey(), c.ShouldNotBeEmpty) + cnf2 := conf.ReadConfig("test") + config3 := cfg.NewConfig(cnf2) + eng, err = config3.GetFileEngines() + c.So(err, c.ShouldBeNil) + c.So(eng, c.ShouldNotBeNil) + c.So(len(eng), c.ShouldBeGreaterThan, 0) + }) } diff --git a/internal/pkg/constant.go b/internal/pkg/constant.go index 3b805c3..fe1b0b5 100644 --- a/internal/pkg/constant.go +++ b/internal/pkg/constant.go @@ -30,14 +30,16 @@ var ( ErrAppXDateMissing = errors.New("app x-date缺失") ErrRequestExpired = errors.New("请求已过期") - ErrUploadNotInitiate = errors.New("上传未初始化") - ErrSHA256NotMatch = errors.New("sha256不匹配") - ErrUploadIdMissing = errors.New("uploadId缺失") - ErrUploadIdNotFound = errors.New("uploadId未找到") - ErrPartNumberMissing = errors.New("partNumber缺失") - ErrInvalidFileKey = errors.New("无效的文件路径") - ErrFileKeyMissing = errors.New("file key 缺失") - ErrInvalidRange = errors.New("无效的range") + ErrUploadNotInitiate = errors.New("上传未初始化") + ErrSHA256NotMatch = errors.New("sha256不匹配") + ErrUploadIdMissing = errors.New("uploadId缺失") + ErrUploadIdNotFound = errors.New("uploadId未找到") + ErrPartNumberMissing = errors.New("partNumber缺失") + ErrInvalidFileKey = errors.New("无效的文件路径") + ErrFileKeyMissing = errors.New("file key 缺失") + ErrInvalidRange = errors.New("无效的range") + ErrBucketNotFound = errors.New("bucket不存在") + ErrFsEngineNotSupport = errors.New("文件引擎不支持") ErrIdentityMissing = errors.New("identity缺失") diff --git a/internal/server/coverprofile.cov b/internal/server/coverprofile.cov index 6168ac0..43ef396 100644 --- a/internal/server/coverprofile.cov +++ b/internal/server/coverprofile.cov @@ -1,17 +1,17 @@ mode: set -github.com/begonia-org/begonia/internal/server/server.go:27.57,34.2 3 1 -github.com/begonia-org/begonia/internal/server/server.go:35.73,38.16 3 1 -github.com/begonia-org/begonia/internal/server/server.go:38.16,40.3 1 0 -github.com/begonia-org/begonia/internal/server/server.go:41.2,42.16 2 1 -github.com/begonia-org/begonia/internal/server/server.go:42.16,44.3 1 0 -github.com/begonia-org/begonia/internal/server/server.go:45.2,46.16 2 1 -github.com/begonia-org/begonia/internal/server/server.go:46.16,48.3 1 0 -github.com/begonia-org/begonia/internal/server/server.go:49.2,49.16 1 1 -github.com/begonia-org/begonia/internal/server/server.go:51.155,84.16 18 1 -github.com/begonia-org/begonia/internal/server/server.go:84.16,85.13 1 0 -github.com/begonia-org/begonia/internal/server/server.go:87.2,88.31 2 1 -github.com/begonia-org/begonia/internal/server/server.go:88.31,90.17 2 1 -github.com/begonia-org/begonia/internal/server/server.go:90.17,91.14 1 0 -github.com/begonia-org/begonia/internal/server/server.go:93.3,93.45 1 1 -github.com/begonia-org/begonia/internal/server/server.go:93.45,95.4 1 1 -github.com/begonia-org/begonia/internal/server/server.go:98.2,100.11 2 1 +github.com/begonia-org/begonia/internal/server/server.go:26.57,33.2 3 1 +github.com/begonia-org/begonia/internal/server/server.go:34.73,37.16 3 1 +github.com/begonia-org/begonia/internal/server/server.go:37.16,39.3 1 1 +github.com/begonia-org/begonia/internal/server/server.go:40.2,41.16 2 1 +github.com/begonia-org/begonia/internal/server/server.go:41.16,43.3 1 1 +github.com/begonia-org/begonia/internal/server/server.go:44.2,45.16 2 1 +github.com/begonia-org/begonia/internal/server/server.go:45.16,47.3 1 1 +github.com/begonia-org/begonia/internal/server/server.go:48.2,48.16 1 1 +github.com/begonia-org/begonia/internal/server/server.go:50.155,76.16 15 1 +github.com/begonia-org/begonia/internal/server/server.go:76.16,77.13 1 1 +github.com/begonia-org/begonia/internal/server/server.go:79.2,87.31 5 1 +github.com/begonia-org/begonia/internal/server/server.go:87.31,89.17 2 1 +github.com/begonia-org/begonia/internal/server/server.go:89.17,90.14 1 1 +github.com/begonia-org/begonia/internal/server/server.go:92.3,92.45 1 1 +github.com/begonia-org/begonia/internal/server/server.go:92.45,94.4 1 1 +github.com/begonia-org/begonia/internal/server/server.go:97.2,99.11 2 1 diff --git a/internal/server/server.go b/internal/server/server.go index 3192c43..2e36021 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -72,17 +72,16 @@ func NewGateway(cfg *gateway.GatewayConfig, conf *config.Config, services []serv // 中间件配置 opts.Options = append(opts.Options, grpc.ChainUnaryInterceptor(pluginApply.UnaryInterceptorChains()...)) opts.Options = append(opts.Options, grpc.ChainStreamInterceptor(pluginApply.StreamInterceptorChains()...)) - + pd, err := readDesc(conf) + if err != nil { + panic(err) + } cors := &gateway.CorsHandler{ Cors: conf.GetCorsConfig(), } opts.HttpHandlers = append(opts.HttpHandlers, cors.Handle) gw := gateway.New(cfg, opts) - pd, err := readDesc(conf) - if err != nil { - panic(err) - } routersList := routers.Get() for _, srv := range services { err := gw.RegisterLocalService(context.Background(), pd, srv.Desc(), srv) diff --git a/internal/server/server_test.go b/internal/server/server_test.go new file mode 100644 index 0000000..1fc45e0 --- /dev/null +++ b/internal/server/server_test.go @@ -0,0 +1,62 @@ +package server + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal/middleware" + cfg "github.com/begonia-org/begonia/internal/pkg/config" + "github.com/begonia-org/begonia/internal/service" + gosdk "github.com/begonia-org/go-sdk" + c "github.com/smartystreets/goconvey/convey" +) + +func TestReadDescErr(t *testing.T) { + c.Convey("TestReadDescErr", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + conf := config.ReadConfig(env) + cnf := cfg.NewConfig(conf) + patch := gomonkey.ApplyFuncReturn(os.ReadFile, []byte{}, fmt.Errorf("read file error")) + defer patch.Reset() + _, err := readDesc(cnf) + patch.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "read file error") + patch1 := gomonkey.ApplyFuncReturn(gateway.NewDescriptionFromBinary, nil, fmt.Errorf("new desc error")) + defer patch1.Reset() + _, err = readDesc(cnf) + patch1.Reset() + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "new desc error") + desc := cnf.GetLocalAPIDesc() + bin, _ := os.ReadFile(desc) + pd, _ := gateway.NewDescriptionFromBinary(bin, filepath.Dir(desc)) + patch2 := gomonkey.ApplyMethodReturn(pd, "SetHttpResponse", fmt.Errorf("set http response error")) + defer patch2.Reset() + _, err = readDesc(cnf) + c.So(err, c.ShouldNotBeNil) + c.So(err.Error(), c.ShouldContainSubstring, "set http response error") + + worker := func() { + NewGateway(NewGatewayConfig(":127.0.0.1:12138"), cnf, []service.Service{&service.SysService{}}, &middleware.PluginsApply{Plugins: make(gosdk.Plugins, 0)}) + } + c.So(worker, c.ShouldPanic) + patch2.Reset() + + patch3 := gomonkey.ApplyFuncReturn((*gateway.GatewayServer).RegisterLocalService, fmt.Errorf("register local service error")) + defer patch3.Reset() + c.So(worker, c.ShouldPanicWith, fmt.Errorf("register local service error")) + patch3.Reset() + + c.So(worker, c.ShouldNotPanic) + }) +} diff --git a/internal/service/base_test.go b/internal/service/base_test.go index 9b897b4..d333d1c 100644 --- a/internal/service/base_test.go +++ b/internal/service/base_test.go @@ -42,14 +42,22 @@ func runExampleServer() { func readInitAPP() { homeDir, err := os.UserHomeDir() if err != nil { - log.Fatalf(err.Error()) + + log.Fatalf("use home idr error: %s", err.Error()) return } + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + op := internal.InitOperatorApp(config.ReadConfig(env)) + _ = op.Init() path := filepath.Join(homeDir, ".begonia") path = filepath.Join(path, "admin-app.json") file, err := os.Open(path) if err != nil { - log.Fatalf(err.Error()) + + log.Fatalf("open file %s error: %s", path, err.Error()) return } @@ -59,7 +67,7 @@ func readInitAPP() { app := &api.Apps{} err = decoder.Decode(app) if err != nil { - log.Fatalf(err.Error()) + log.Fatalf("decode file %s error: %s", path, err.Error()) return } diff --git a/internal/service/file.go b/internal/service/file.go index 12cf064..15ce061 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -26,37 +26,77 @@ import ( type FileService struct { api.UnimplementedFileServiceServer - biz *file.FileUsecase + biz map[string]file.FileUsecase config *config.Config } -func NewFileService(biz *file.FileUsecase, config *config.Config) api.FileServiceServer { +func NewFileService(biz map[string]file.FileUsecase, config *config.Config) api.FileServiceServer { return &FileService{biz: biz, config: config} } +type request interface { + GetEngine() string +} + +func (f *FileService) checkFsEngine(engine string) error { + if e, ok := f.biz[engine]; !ok || e == nil { + return gosdk.NewError(pkg.ErrFsEngineNotSupport, int32(api.FileSvrStatus_FILE_INVALIDATE_ENGINE_ERR), codes.InvalidArgument, "not_support_engine") + } + return nil +} +func (f *FileService) serviceDecorator(_ context.Context, req request, handle func() (interface{}, error)) (interface{}, error) { + if err := f.checkFsEngine(req.GetEngine()); err != nil { + return nil, err + } + return handle() + +} func (f *FileService) Upload(ctx context.Context, in *api.UploadFileRequest) (*api.UploadFileResponse, error) { identity := "" if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - return f.biz.Upload(ctx, in, identity) + + rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.GetEngine()].Upload(ctx, in, identity) + }) + r, _ := rsp.(*api.UploadFileResponse) + return r, err + } func (f *FileService) InitiateMultipartUpload(ctx context.Context, in *api.InitiateMultipartUploadRequest) (*api.InitiateMultipartUploadResponse, error) { - return f.biz.InitiateUploadFile(ctx, in) + rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].InitiateUploadFile(ctx, in) + }) + r, _ := rsp.(*api.InitiateMultipartUploadResponse) + return r, err } func (f *FileService) UploadMultipartFile(ctx context.Context, in *api.UploadMultipartFileRequest) (*api.UploadMultipartFileResponse, error) { - return f.biz.UploadMultipartFileFile(ctx, in) + rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].UploadMultipartFileFile(ctx, in) + }) + r, _ := rsp.(*api.UploadMultipartFileResponse) + return r, err } func (f *FileService) CompleteMultipartUpload(ctx context.Context, in *api.CompleteMultipartUploadRequest) (*api.CompleteMultipartUploadResponse, error) { identity := "" if identity = GetIdentity(ctx); identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - return f.biz.CompleteMultipartUploadFile(ctx, in, identity) + rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].CompleteMultipartUploadFile(ctx, in, identity) + }) + r, _ := rsp.(*api.CompleteMultipartUploadResponse) + return r, err + } func (f *FileService) AbortMultipartUpload(ctx context.Context, in *api.AbortMultipartUploadRequest) (*api.AbortMultipartUploadResponse, error) { - return f.biz.AbortMultipartUpload(ctx, in) + rsp, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].AbortMultipartUpload(ctx, in) + }) + r, _ := rsp.(*api.AbortMultipartUploadResponse) + return r, err } func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*httpbody.HttpBody, error) { identity := "" @@ -69,10 +109,14 @@ func (f *FileService) Download(ctx context.Context, in *api.DownloadRequest) (*h return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "url_unescape") } in.Key = newKey - buf, err := f.biz.Download(ctx, in, identity) + r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].Download(ctx, in, identity) + }) if err != nil { return nil, err } + // return r,err + buf, _ := r.([]byte) shaer := sha256.New() shaer.Write(buf) @@ -153,8 +197,10 @@ func (f *FileService) DownloadForRange(ctx context.Context, in *api.DownloadRequ return nil, gosdk.NewError(err, int32(common.Code_UNKNOWN), codes.InvalidArgument, "parse_range_header") } } - - data, fileSize, err := f.biz.DownloadForRange(ctx, in, start, end, identity) + if err := f.checkFsEngine(in.Engine); err != nil { + return nil, err + } + data, fileSize, err := f.biz[in.Engine].DownloadForRange(ctx, in, start, end, identity) if err != nil { return nil, err } @@ -180,18 +226,25 @@ func (f *FileService) Delete(ctx context.Context, in *api.DeleteRequest) (*api.D if identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - return f.biz.Delete(ctx, in, identity) + r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].Delete(ctx, in, identity) + }) + rsp, _ := r.(*api.DeleteResponse) + return rsp, err } func (f *FileService) Metadata(ctx context.Context, in *api.FileMetadataRequest) (*api.FileMetadataResponse, error) { identity := GetIdentity(ctx) if identity == "" { return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") } - rsp, err := f.biz.Metadata(ctx, in, identity) + r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].Metadata(ctx, in, identity) + }) if err != nil { return nil, err - } + rsp, _ := r.(*api.FileMetadataResponse) + timestamp := time.UnixMilli(rsp.ModifyTime) lastModified := timestamp.UTC().Format(time.RFC1123) @@ -222,6 +275,27 @@ func (f *FileService) Metadata(ctx context.Context, in *api.FileMetadataRequest) return rsp, err } +func (f *FileService) ListFiles(ctx context.Context, in *api.ListFilesRequest) (*api.ListFilesResponse, error) { + identity := GetIdentity(ctx) + if identity == "" { + return nil, gosdk.NewError(pkg.ErrIdentityMissing, int32(user.UserSvrCode_USER_IDENTITY_MISSING_ERR), codes.InvalidArgument, "not_found_identity") + } + r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].List(ctx, in, identity) + }) + if err != nil { + return nil, err + } + files, _ := r.([]*api.Files) + return &api.ListFilesResponse{Files: files}, err +} +func (f *FileService) MakeBucket(ctx context.Context, in *api.MakeBucketRequest) (*api.MakeBucketResponse, error) { + r, err := f.serviceDecorator(ctx, in, func() (interface{}, error) { + return f.biz[in.Engine].MakeBucket(ctx, in) + }) + rsp, _ := r.(*api.MakeBucketResponse) + return rsp, err +} func (f *FileService) Desc() *grpc.ServiceDesc { return &api.FileService_ServiceDesc } diff --git a/internal/service/file_test.go b/internal/service/file_test.go index 506cbdd..b7afa02 100644 --- a/internal/service/file_test.go +++ b/internal/service/file_test.go @@ -13,13 +13,17 @@ import ( "path/filepath" "runtime" "testing" + "time" "github.com/agiledragon/gomonkey/v2" "github.com/begonia-org/begonia" "github.com/begonia-org/begonia/config" "github.com/begonia-org/begonia/gateway" "github.com/begonia-org/begonia/internal/biz/file" + "github.com/begonia-org/begonia/internal/data" + "github.com/begonia-org/begonia/internal/pkg" + cfg "github.com/begonia-org/begonia/internal/pkg/config" "github.com/begonia-org/begonia/internal/service" api "github.com/begonia-org/go-sdk/api/file/v1" @@ -29,6 +33,8 @@ import ( "google.golang.org/grpc/metadata" ) +var fileBucket = "" + func sumFileSha256(src string) (string, error) { file, err := os.Open(src) if err != nil { @@ -43,7 +49,30 @@ func sumFileSha256(src string) (string, error) { return hex.EncodeToString(hash.Sum(nil)), nil } +func newFileUsecases() map[string]file.FileUsecase { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + conf := cfg.NewConfig(cnf) + repo := data.NewFileRepo(cnf, gateway.Log) + return file.NewFileUsecase(conf, repo) +} +func makeBucket(t *testing.T) { + c.Convey("test make bucket", t, func() { + fileBucket = fmt.Sprintf("test-service-bucket-%s", time.Now().Format("20060102150405")) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) + rsp, err := apiClient.CreateBucket(context.Background(), fileBucket, "test", false) + c.So(err, c.ShouldBeNil) + c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + minioFile := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_MINIO) + rsp, err = minioFile.CreateBucket(context.Background(), fileBucket, "test", false) + c.So(err, c.ShouldBeNil) + c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + }) +} func upload(t *testing.T) { env := begonia.Env if env == "" { @@ -51,20 +80,18 @@ func upload(t *testing.T) { } c.Convey("test upload file", t, func() { // test upload file - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) _, filename, _, _ := runtime.Caller(0) pbFile := filepath.Join(filepath.Dir(filepath.Dir(filepath.Dir(filename))), "testdata", "helloworld.pb") srcSha256, _ := sumFileSha256(pbFile) - rsp, err := apiClient.UploadFile(context.Background(), pbFile, "test/helloworld.pb", true) + rsp, err := apiClient.UploadFile(context.Background(), pbFile, "test/helloworld.pb", fileBucket, true) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) c.So(rsp.Uri, c.ShouldNotBeEmpty) conf := cfg.NewConfig(config.ReadConfig(env)) - filePath := filepath.Join(conf.GetUploadDir(), rsp.Uri) - // filename = filepath.Base(rsp.Uri) - // filePath := filepath.Join(saveDir, filename) + filePath := filepath.Join(conf.GetUploadDir(), fileBucket, rsp.Uri) _, err = os.Stat(filePath) c.So(err, c.ShouldBeNil) @@ -72,7 +99,6 @@ func upload(t *testing.T) { c.So(err, c.ShouldBeNil) c.So(srcSha256, c.ShouldEqual, dstSha256) c.So(rsp.Version, c.ShouldNotBeEmpty) - // c.So(info.Size(), c., 102) }) } @@ -127,12 +153,12 @@ func generateRandomFile(size int64) (*TmpFile, error) { } func uploadParts(t *testing.T) { c.Convey("test upload file", t, func() { - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) var err error tmpFile, err = generateRandomFile(1024 * 1024 * 2) c.So(err, c.ShouldBeNil) defer os.Remove(tmpFile.path) - rsp, err := apiClient.UploadFileWithMuiltParts(context.Background(), tmpFile.path, "test/tmp.bin", true) + rsp, err := apiClient.UploadFileWithMuiltParts(context.Background(), tmpFile.path, "test/tmp.bin", fileBucket, true) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) c.So(rsp.Uri, c.ShouldNotBeEmpty) @@ -144,7 +170,7 @@ func uploadParts(t *testing.T) { } conf := cfg.NewConfig(config.ReadConfig(env)) - filePath := filepath.Join(conf.GetUploadDir(), rsp.Uri) + filePath := filepath.Join(conf.GetUploadDir(), fileBucket, rsp.Uri) file, err := os.Open(filePath) c.So(err, c.ShouldBeNil) @@ -174,12 +200,12 @@ func uploadParts(t *testing.T) { } func download(t *testing.T) { c.Convey("test download file", t, func() { - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) tmp, err := os.CreateTemp("", "testfile-*.txt") c.So(err, c.ShouldBeNil) defer tmp.Close() defer os.Remove(tmp.Name()) - sha256Str, err := apiClient.DownloadFile(context.Background(), sdkAPPID+"/test/helloworld.pb", tmp.Name(), "") + sha256Str, err := apiClient.DownloadFile(context.Background(), sdkAPPID+"/test/helloworld.pb", tmp.Name(), "", fileBucket) c.So(err, c.ShouldBeNil) _, err = os.Stat(tmp.Name()) c.So(err, c.ShouldBeNil) @@ -191,12 +217,12 @@ func download(t *testing.T) { } func downloadParts(t *testing.T) { c.Convey("test download parts file", t, func() { - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) tmp, err := os.CreateTemp("", "testfile-*.txt") c.So(err, c.ShouldBeNil) defer tmp.Close() defer os.Remove(tmp.Name()) - rsp, err := apiClient.DownloadMultiParts(context.Background(), sdkAPPID+"/test/tmp.bin", tmp.Name(), "") + rsp, err := apiClient.DownloadMultiParts(context.Background(), sdkAPPID+"/test/tmp.bin", tmp.Name(), "", fileBucket) c.So(err, c.ShouldBeNil) // c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) downloadedSha256, _ := sumFileSha256(tmp.Name()) @@ -211,8 +237,8 @@ func deleteFile(t *testing.T) { env = begonia.Env } c.Convey("test delete file", t, func() { - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) - rsp, err := apiClient.DeleteFile(context.Background(), sdkAPPID+"/test/helloworld.pb") + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) + rsp, err := apiClient.DeleteFile(context.Background(), sdkAPPID+"/test/helloworld.pb", fileBucket) c.So(err, c.ShouldBeNil) c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) conf := cfg.NewConfig(config.ReadConfig(env)) @@ -229,19 +255,25 @@ func deleteFile(t *testing.T) { } func testRangeDownload(t *testing.T) { c.Convey("test range download file", t, func() { - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) tmp, err := os.CreateTemp("", "testfile-*.txt") c.So(err, c.ShouldBeNil) defer tmp.Close() defer os.Remove(tmp.Name()) - rsp, err := apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", -1, 128) + rsp, err := apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", -1, 128, fileBucket) c.So(err, c.ShouldBeNil) c.So(len(rsp), c.ShouldEqual, 129) - rsp, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1) + rsp, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1, fileBucket) c.So(err, c.ShouldBeNil) c.So(len(rsp), c.ShouldEqual, 1024*1024*2-128) + patch := gomonkey.ApplyFuncReturn(service.GetIdentity, "") + defer patch.Reset() + _, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1, fileBucket) + c.So(err, c.ShouldNotBeNil) + patch.Reset() + }) } @@ -344,14 +376,15 @@ func testRangeDownloadErr(t *testing.T) { c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, cs.err.Error()) } + fileUseCase := newFileUsecases() - patch := gomonkey.ApplyFuncReturn((*file.FileUsecase).DownloadForRange, nil, int64(0), fmt.Errorf("test download for range error")) + patch := gomonkey.ApplyMethodReturn(fileUseCase[api.FileEngine_FILE_ENGINE_LOCAL.String()], "DownloadForRange", nil, int64(0), fmt.Errorf("test download for range error")) defer patch.Reset() md := metadata.New(nil) md.Set("x-identity", sdkAPPID) md.Set("range", "bytes=0-0") ctx := metadata.NewIncomingContext(context.Background(), md) - _, err := srv.DownloadForRange(ctx, &api.DownloadRequest{Key: sdkAPPID + "/test/helloworld.pb"}) + _, err := srv.DownloadForRange(ctx, &api.DownloadRequest{Key: sdkAPPID + "/test/helloworld.pb", Engine: api.FileEngine_FILE_ENGINE_LOCAL.String(), Bucket: fileBucket}) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "test download for range error") patch.Reset() @@ -361,20 +394,9 @@ func testRangeDownloadErr(t *testing.T) { func testAbortUpload(t *testing.T) { c.Convey("test range download file", t, func() { - apiClient := client.NewFilesAPI(apiAddr, accessKey, secret) + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) _, err := apiClient.AbortUpload(context.Background(), "test/tmp.bindddasd") c.So(err, c.ShouldNotBeNil) - // tmp, err := os.CreateTemp("", "testfile-*.txt") - // c.So(err, c.ShouldBeNil) - // defer tmp.Close() - // defer os.Remove(tmp.Name()) - // rsp, err := apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", -1, 128) - // c.So(err, c.ShouldBeNil) - // c.So(len(rsp), c.ShouldEqual, 129) - - // rsp, err = apiClient.RangeDownload(context.Background(), sdkAPPID+"/test/tmp.bin", "", 128, -1) - // c.So(err, c.ShouldBeNil) - // c.So(len(rsp), c.ShouldEqual, 1024*1024*2-128) }) } @@ -405,23 +427,52 @@ func testMetaErr(t *testing.T) { _, err := srv.Metadata(ctx, &api.FileMetadataRequest{}) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, pkg.ErrIdentityMissing.Error()) + fileUseCase := newFileUsecases() - patch := gomonkey.ApplyFuncReturn((*file.FileUsecase).Metadata, nil, fmt.Errorf("test metadata error")) + patch := gomonkey.ApplyMethodReturn(fileUseCase[api.FileEngine_FILE_ENGINE_LOCAL.String()], "Metadata", nil, fmt.Errorf("test metadata error")) defer patch.Reset() ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs("x-identity", sdkAPPID)) - _, err = srv.Metadata(ctx, &api.FileMetadataRequest{Key: sdkAPPID + "/test/helloworld.pb"}) + _, err = srv.Metadata(ctx, &api.FileMetadataRequest{Key: sdkAPPID + "/test/helloworld.pb", Bucket: fileBucket, Engine: api.FileEngine_FILE_ENGINE_LOCAL.String()}) c.So(err, c.ShouldNotBeNil) c.So(err.Error(), c.ShouldContainSubstring, "test metadata error") }) } +func testFileList(t *testing.T) { + c.Convey("test file list", t, func() { + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) + rsp, err := apiClient.ListFiles(context.Background(), api.FileEngine_FILE_ENGINE_LOCAL.String(), fileBucket, 1, 10) + c.So(err, c.ShouldBeNil) + c.So(rsp.StatusCode, c.ShouldEqual, common.Code_OK) + }) + c.Convey("test file list err", t, func() { + apiClient := client.NewFilesAPI(apiAddr, accessKey, secret, api.FileEngine_FILE_ENGINE_LOCAL) + fileUseCase := newFileUsecases() + patch := gomonkey.ApplyMethodReturn(fileUseCase[api.FileEngine_FILE_ENGINE_LOCAL.String()], "List", nil, fmt.Errorf("test metadata error")) + defer patch.Reset() + _, err := apiClient.ListFiles(context.Background(), api.FileEngine_FILE_ENGINE_LOCAL.String(), fileBucket, 1, 10) + c.So(err, c.ShouldNotBeNil) + patch.Reset() + patch2 := gomonkey.ApplyFuncReturn(service.GetIdentity, "") + defer patch2.Reset() + _, err = apiClient.ListFiles(context.Background(), api.FileEngine_FILE_ENGINE_LOCAL.String(), fileBucket, 1, 10) + c.So(err, c.ShouldNotBeNil) + patch2.Reset() + + _, err = apiClient.ListFiles(context.Background(), "api.FileEngine_FILE_ENGINE_LOCAL.String()", fileBucket, 1, 10) + c.So(err, c.ShouldNotBeNil) + + }) +} func TestFile(t *testing.T) { + t.Run("makeBucket", makeBucket) t.Run("upload", upload) t.Run("download", download) t.Run("testUploadErr", testUploadErr) t.Run("testDownloadErr", testDownloadErr) t.Run("uploadParts", uploadParts) t.Run("testRangeDownload", testRangeDownload) + t.Run("testFileList", testFileList) t.Run("testAbortUpload", testAbortUpload) t.Run("testRangeDownloadErr", testRangeDownloadErr) t.Run("downloadParts", downloadParts) diff --git a/internal/service/wire.go b/internal/service/wire.go index 9b0b457..0d8fddd 100644 --- a/internal/service/wire.go +++ b/internal/service/wire.go @@ -30,7 +30,7 @@ func NewEndpointSvrForTest(config *tiga.Configuration, log logger.Logger) ep.End panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, NewEndpointsService)) } func NewFileSvrForTest(config *tiga.Configuration, log logger.Logger) file.FileServiceServer { - panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, NewFileService)) + panic(wire.Build(biz.ProviderSet, data.ProviderSet, pkg.ProviderSet, NewFileService)) } func NewSysSvrForTest(config *tiga.Configuration, log logger.Logger) sys.SystemServiceServer { panic(wire.Build(NewSysService)) diff --git a/internal/service/wire_gen.go b/internal/service/wire_gen.go index 6cd8f92..a269815 100644 --- a/internal/service/wire_gen.go +++ b/internal/service/wire_gen.go @@ -59,16 +59,21 @@ func NewEndpointSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1_3. dataData := data.NewData(mySQLDao, redisDao, etcdDao) configConfig := config.NewConfig(config2) endpointRepo := data.NewEndpointRepoImpl(dataData, configConfig) - fileUsecase := file.NewFileUsecase(configConfig) - endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, fileUsecase, configConfig) + endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, configConfig) endpointServiceServer := NewEndpointsService(endpointUsecase, log, configConfig) return endpointServiceServer } func NewFileSvrForTest(config2 *tiga.Configuration, log logger.Logger) v1_4.FileServiceServer { configConfig := config.NewConfig(config2) - fileUsecase := file.NewFileUsecase(configConfig) - fileServiceServer := NewFileService(fileUsecase, configConfig) + mySQLDao := data.NewMySQL(config2) + redisDao := data.NewRDB(config2) + etcdDao := data.NewEtcd(config2) + dataData := data.NewData(mySQLDao, redisDao, etcdDao) + curd := data.NewCurdImpl(mySQLDao, configConfig) + fileRepo := data.NewFileRepoImpl(dataData, curd) + v := file.NewFileUsecase(configConfig, fileRepo) + fileServiceServer := NewFileService(v, configConfig) return fileServiceServer } diff --git a/internal/wire.go b/internal/wire.go index 865f0b5..e70bad6 100644 --- a/internal/wire.go +++ b/internal/wire.go @@ -47,8 +47,11 @@ func NewEndpointSvr(config *tiga.Configuration, log logger.Logger) ep.EndpointSe panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, service.ProviderSet)) } func NewFileSvr(config *tiga.Configuration, log logger.Logger) file.FileServiceServer { - panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, service.ProviderSet)) + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, service.ProviderSet)) } func NewSysSvr(config *tiga.Configuration, log logger.Logger) sys.SystemServiceServer { panic(wire.Build(service.ProviderSet)) } +func NewWorker(config *tiga.Configuration, log logger.Logger, gw string) GatewayWorker { + panic(wire.Build(biz.ProviderSet, pkg.ProviderSet, data.ProviderSet, service.ProviderSet, daemon.ProviderSet, server.ProviderSet, middleware.ProviderSet, NewGatewayWorkerImpl)) +} diff --git a/internal/wire_gen.go b/internal/wire_gen.go index 26deeab..0b4bee0 100644 --- a/internal/wire_gen.go +++ b/internal/wire_gen.go @@ -57,22 +57,23 @@ func New(config2 *tiga.Configuration, log logger.Logger, endpoint2 string) Gatew dataOperatorUsecase := biz.NewDataOperatorUsecase(dataOperatorRepo, configConfig, log, endpointWatcher, endpointRepo) daemonDaemon := daemon.NewDaemonImpl(configConfig, dataOperatorUsecase) gatewayConfig := server.NewGatewayConfig(endpoint2) - fileUsecase := file.NewFileUsecase(configConfig) - fileServiceServer := service.NewFileService(fileUsecase, configConfig) + fileRepo := data.NewFileRepoImpl(dataData, curd) + v := file.NewFileUsecase(configConfig, fileRepo) + fileServiceServer := service.NewFileService(v, configConfig) usersAuth := crypto.NewUsersAuth(configConfig) authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) authServiceServer := service.NewAuthzService(authzUsecase, log, usersAuth, configConfig) - endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, fileUsecase, configConfig) + endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, configConfig) endpointServiceServer := service.NewEndpointsService(endpointUsecase, log, configConfig) appUsecase := biz.NewAppUsecase(appRepo, configConfig) appsServiceServer := service.NewAppService(appUsecase, log, configConfig) systemServiceServer := service.NewSysService() userUsecase := biz.NewUserUsecase(userRepo, configConfig) userServiceServer := service.NewUserService(userUsecase, log, configConfig) - v := service.NewServices(fileServiceServer, authServiceServer, endpointServiceServer, appsServiceServer, systemServiceServer, userServiceServer) + v2 := service.NewServices(fileServiceServer, authServiceServer, endpointServiceServer, appsServiceServer, systemServiceServer, userServiceServer) accessKeyAuth := biz.NewAccessKeyAuth(appRepo, configConfig, log) pluginsApply := middleware.New(configConfig, redisDao, authzUsecase, log, accessKeyAuth) - gatewayServer := server.NewGateway(gatewayConfig, configConfig, v, pluginsApply) + gatewayServer := server.NewGateway(gatewayConfig, configConfig, v2, pluginsApply) gatewayWorker := NewGatewayWorkerImpl(daemonDaemon, gatewayServer) return gatewayWorker } @@ -112,16 +113,21 @@ func NewEndpointSvr(config2 *tiga.Configuration, log logger.Logger) v1_3.Endpoin dataData := data.NewData(mySQLDao, redisDao, etcdDao) configConfig := config.NewConfig(config2) endpointRepo := data.NewEndpointRepoImpl(dataData, configConfig) - fileUsecase := file.NewFileUsecase(configConfig) - endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, fileUsecase, configConfig) + endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, configConfig) endpointServiceServer := service.NewEndpointsService(endpointUsecase, log, configConfig) return endpointServiceServer } func NewFileSvr(config2 *tiga.Configuration, log logger.Logger) v1_4.FileServiceServer { configConfig := config.NewConfig(config2) - fileUsecase := file.NewFileUsecase(configConfig) - fileServiceServer := service.NewFileService(fileUsecase, configConfig) + mySQLDao := data.NewMySQL(config2) + redisDao := data.NewRDB(config2) + etcdDao := data.NewEtcd(config2) + dataData := data.NewData(mySQLDao, redisDao, etcdDao) + curd := data.NewCurdImpl(mySQLDao, configConfig) + fileRepo := data.NewFileRepoImpl(dataData, curd) + v := file.NewFileUsecase(configConfig, fileRepo) + fileServiceServer := service.NewFileService(v, configConfig) return fileServiceServer } @@ -129,3 +135,41 @@ func NewSysSvr(config2 *tiga.Configuration, log logger.Logger) v1_5.SystemServic systemServiceServer := service.NewSysService() return systemServiceServer } + +func NewWorker(config2 *tiga.Configuration, log logger.Logger, gw string) GatewayWorker { + configConfig := config.NewConfig(config2) + mySQLDao := data.NewMySQL(config2) + redisDao := data.NewRDB(config2) + etcdDao := data.NewEtcd(config2) + dataData := data.NewData(mySQLDao, redisDao, etcdDao) + curd := data.NewCurdImpl(mySQLDao, configConfig) + layeredCache := data.NewLayeredCache(redisDao, configConfig, log) + appRepo := data.NewAppRepoImpl(curd, layeredCache, configConfig) + userRepo := data.NewUserRepoImpl(dataData, layeredCache, curd, configConfig) + authzRepo := data.NewAuthzRepoImpl(log, layeredCache) + dataOperatorRepo := data.NewDataOperatorRepo(dataData, appRepo, userRepo, authzRepo, layeredCache, log) + endpointRepo := data.NewEndpointRepoImpl(dataData, configConfig) + endpointWatcher := endpoint.NewWatcher(configConfig, endpointRepo) + dataOperatorUsecase := biz.NewDataOperatorUsecase(dataOperatorRepo, configConfig, log, endpointWatcher, endpointRepo) + daemonDaemon := daemon.NewDaemonImpl(configConfig, dataOperatorUsecase) + gatewayConfig := server.NewGatewayConfig(gw) + fileRepo := data.NewFileRepoImpl(dataData, curd) + v := file.NewFileUsecase(configConfig, fileRepo) + fileServiceServer := service.NewFileService(v, configConfig) + usersAuth := crypto.NewUsersAuth(configConfig) + authzUsecase := biz.NewAuthzUsecase(authzRepo, userRepo, log, usersAuth, configConfig) + authServiceServer := service.NewAuthzService(authzUsecase, log, usersAuth, configConfig) + endpointUsecase := endpoint.NewEndpointUsecase(endpointRepo, configConfig) + endpointServiceServer := service.NewEndpointsService(endpointUsecase, log, configConfig) + appUsecase := biz.NewAppUsecase(appRepo, configConfig) + appsServiceServer := service.NewAppService(appUsecase, log, configConfig) + systemServiceServer := service.NewSysService() + userUsecase := biz.NewUserUsecase(userRepo, configConfig) + userServiceServer := service.NewUserService(userUsecase, log, configConfig) + v2 := service.NewServices(fileServiceServer, authServiceServer, endpointServiceServer, appsServiceServer, systemServiceServer, userServiceServer) + accessKeyAuth := biz.NewAccessKeyAuth(appRepo, configConfig, log) + pluginsApply := middleware.New(configConfig, redisDao, authzUsecase, log, accessKeyAuth) + gatewayServer := server.NewGateway(gatewayConfig, configConfig, v2, pluginsApply) + gatewayWorker := NewGatewayWorkerImpl(daemonDaemon, gatewayServer) + return gatewayWorker +} diff --git a/internal/wire_gen_test.go b/internal/wire_gen_test.go new file mode 100644 index 0000000..d598a6c --- /dev/null +++ b/internal/wire_gen_test.go @@ -0,0 +1,52 @@ +package internal_test + +import ( + "context" + "testing" + + "github.com/agiledragon/gomonkey/v2" + "github.com/begonia-org/begonia" + "github.com/begonia-org/begonia/config" + "github.com/begonia-org/begonia/gateway" + "github.com/begonia-org/begonia/internal" + "github.com/begonia-org/begonia/internal/daemon" + "github.com/begonia-org/begonia/internal/middleware" + cfg "github.com/begonia-org/begonia/internal/pkg/config" + "github.com/begonia-org/begonia/internal/server" + "github.com/begonia-org/begonia/internal/service" + c "github.com/smartystreets/goconvey/convey" +) + +func TestNewSrv(t *testing.T) { + c.Convey("TestNewSrv", t, func() { + env := "dev" + if begonia.Env != "" { + env = begonia.Env + } + cnf := config.ReadConfig(env) + srv1 := internal.NewAPPSvr(cnf, gateway.Log) + c.So(srv1, c.ShouldNotBeNil) + srv2 := internal.NewAuthzSvr(cnf, gateway.Log) + c.So(srv2, c.ShouldNotBeNil) + srv3 := internal.NewEndpointSvr(cnf, gateway.Log) + c.So(srv3, c.ShouldNotBeNil) + srv4 := internal.NewFileSvr(cnf, gateway.Log) + c.So(srv4, c.ShouldNotBeNil) + srv5 := internal.NewSysSvr(cnf, gateway.Log) + c.So(srv5, c.ShouldNotBeNil) + srv6 := internal.InitOperatorApp(cnf) + c.So(srv6, c.ShouldNotBeNil) + srv7 := internal.NewWorker(cnf, gateway.Log, "127.0.0.1:12148") + c.So(srv7, c.ShouldNotBeNil) + patch := gomonkey.ApplyFunc((*daemon.DaemonImpl).Start, func(_ *daemon.DaemonImpl, _ context.Context) {}) + patch = patch.ApplyFunc((*gateway.GatewayServer).Start, func(_ *gateway.GatewayServer) {}) + patch = patch.ApplyFunc(server.NewGateway, func(_ *gateway.GatewayConfig, _ *cfg.Config, _ []service.Service, _ *middleware.PluginsApply) *gateway.GatewayServer { + return &gateway.GatewayServer{} + }) + defer patch.Reset() + go srv7.Start() + srv := internal.New(cnf, gateway.Log, "127.0.0.1:12138") + c.So(srv, c.ShouldNotBeNil) + + }) +} From c8a5703cb15ad60839c591979dfa235c6cd1535b Mon Sep 17 00:00:00 2001 From: "vforfreedom96@gmail.com" Date: Sun, 16 Jun 2024 00:18:04 +0800 Subject: [PATCH 05/16] chore:update arch image --- docs/begonia.png | Bin 272413 -> 271828 bytes gateway/middlewares_test.go | 65 ++++++++++++++++++++++++++++++++++++ gateway/websocket_test.go | 25 ++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 gateway/middlewares_test.go create mode 100644 gateway/websocket_test.go diff --git a/docs/begonia.png b/docs/begonia.png index 1db672eb515739830c73aa0f7b53aee58e6f26e2..43214521d571841c6aea1907a1ac165a27897cd8 100644 GIT binary patch delta 144335 zcmeFZc{r49-#A_(qC&PJOSzFfA!RS2$i8N+RJJ17*DFbjHOZC`8pbw~eJ|UTZL-T) zitNkS#xP^d_nfv{_wRk*@AEw0_qm^ce#hZBjB#D(=W~Ac?L3n{GIX~v+%4J(e*t|m zigDbrrQ;LGO!Mxp+p9-+X_{edS+uOmhHrghF7H%@(ZFdaXphm(@R=g0IF1PkslI*i zSv5r8=(&_&D&<)r!O05k7tsktr(N5VPZQ>d3zOw2>4fTDD-r?IdhG$0d04(Y6X?s2Jn3$Kdw759cc`Iwctz=h=WG$N`D1yR@wBm~jdl(BA__7-p9$HvS@ zd~6Q;Evjk`b+ORgd=+uMEy;s$p>oTuHnzCXG#x^`pf{|V8=X6kaa<8@#tXh1iB%-KM+Og$P zo8Lix=Ik!+twok%hPn{MDzq^ky{8t8@B#X}>?NF(u!ZfRE|%9S-q##=^=Y71sI`C|*{FU%)`dc=@j4vCDnL@J&kq zDVI=az9H9xR9u@~pLZsex9aQDh|Z73oYkzw_Z%SAxUKgV^Zwy$Zku^Oq;qnGSwmAU zM|4)>%9m9fOcFJ>rVyj6=L}KO5?xZ$&X$_EgwxTmNIQ+U@20#T!mfOWZYM<&ACF@_GwUn>0<%`ulH*jP$M3O$#dc@261+I(ZXz=a|gxfPyTFX~M zP^>%bV>b>_p|Iey_yG@H>UFoqQ@_bO>8Dp^lM;vA4~d)mmP@}Hi>mP-w|1%Tc;>UMk7Gp#Sl+dTq62z$#kKCs$!8(PQ0fXpN)KMk)=`_eRo@Wv)Sw-kG7RLox_?@KGU6zfMluM2 zOqhiaG5V_T*=ewOJE>45uQQ=l_zh${|sCB@J3)rzqn(cxo7XQT0j_UZJ@Ys!Pf+jc`=H~4ZeMCjIypaYaJhuL)IhH#c^8XN zHt{rRltlCuh(`NvT1peyrh(>+l1x7@w_vh3-8+di2}N`&w3)AxbbSb^&`q}Uv@9_0 z2btWvJ!?DZ`EWy?_2<=9^9q_eq0*a_QqDldCyEl0hio?sko46CJ3hbNWo;&>&rdX2 z9>E)z<}X=8mh)xl10y?a+d`8(@HeVgS9dMZiO+dcTD{rC_c=!-NU~wxN1bI<=fqkU z%pzvh25ub*Zt9FuwKTy)?=Y8a=X-9Wb{{o-7U8U^WH)lV-stK>&R)d)-fs72=D1