From 6815c3816a0abc859bfb78ec3e97b55da01907fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Garc=C3=ADa?= Date: Wed, 23 Jun 2021 12:17:01 +0200 Subject: [PATCH] Initial commit --- assets/diagram.jpg | Bin 0 -> 60666 bytes docker-compose.yaml | 45 ++ existingApis.yaml | 65 +++ readme.md | 33 ++ shared/grafana/dashboards/dashboard.yml | 12 + .../dashboards/performance-test-dasboard.json | 444 ++++++++++++++++++ shared/grafana/datasources/datasource.yml | 10 + shared/k6/test.js | 73 +++ shared/simulado/mocks.json | 77 +++ similarProducts.yaml | 45 ++ 10 files changed, 804 insertions(+) create mode 100644 assets/diagram.jpg create mode 100644 docker-compose.yaml create mode 100644 existingApis.yaml create mode 100644 readme.md create mode 100644 shared/grafana/dashboards/dashboard.yml create mode 100644 shared/grafana/dashboards/performance-test-dasboard.json create mode 100644 shared/grafana/datasources/datasource.yml create mode 100644 shared/k6/test.js create mode 100644 shared/simulado/mocks.json create mode 100644 similarProducts.yaml diff --git a/assets/diagram.jpg b/assets/diagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa652056296483b87e75e3ca48622eef9b9bd044 GIT binary patch literal 60666 zcmeFYcUV(f_bwU)1f)rquF@0)M4Hs72#5%g-ieAx6Hs~&qJVS(0i{JiKuSPLKx(9e z^coyG)f|7-T)D92?04S)*qy1yR|Mj9cM;_yO8d|yw^yCxZ7XjxeC@IfTQT{P% z^4Y=U-vLxC)T~!z?w)5ecuI5iHT$hMi8-`F_bOUA42QA8vd=ui=q_+_UE<~u5f!_3 zU0hCHLGiYdvc`Q)E$s(74~>jXOwG(KEbX4#zi@DLa`yD{_VM-e4|w}7JRbq0_74t^@W&^A$VCC5{7@_XG?3-;(Tq2=+hang%dX zQjjN)k_7++oEhatUjzJ~_Wv0jP`<;b*_|)^=I1=IE+fZk^MIdD2-qM7HzEPTJ<)?F z)LkS%FakeOm;hW-Lc5=wCX)b~JeWokoT7!m%^%pbupmWuj7>=AoJ7Btjgf(?3aBz# z41_fb?XgLjQd`ewY{xl!FDZpR=Y~eBks}^ipaMLL0jWJiEmz$ z0Q2rdIY?_AcIE8cA_>quPjKmn4^DHLVWDj#fWUgdQ5dlWQ%&w~{-^5D6RS)S^cq9_SKkbp8lhNOU}TO>ed0^UrWj|BK)4LL%Q0CE1d`M6|E za|TXA3#&^|#=5Kr)MWV}KU7mOh(MQG-H^GdrEc*H zIM+4nu3^HpC>HvB_`ua7=ry`#WdAMCx?-DY?M(}AndK;G$mG#;yVu;^9opm9NPt23 zkyHco^uU^@`){&p{ygu0?Y-zm$Q8VqF*Xoi)7w6+HXm+yUCh;k9-rM|7n>;bx$WhJ zd~>!;#zHlTC~Wx_HyV7&Z>60_Kcjf{D4Ty|zl2doRnMhFQ~v9g=W9=zkK7@5Otvbr zRzz^2+6v$0vPb~0>iPR(mO9`JoQm{c=Rgo!7NkT1-1$KQ?9`F~Jd>_2Ae*Oh$&i=w zW}&l^QJ;hMXrC0^`8@kwhSEnSiTYK2?G_7mULQLj4gaL-j-EASX81YkoM`TRIMP%> zRor!%^(lYm%g2+wuO})`;8f+C$e9?UBC%f?&8Q>gf6@6$qZqD47uOX@6j-0WR5Shr z=Xir>;NV64upv#-GaCv9yF@DC*O5<{m^pNR0tMs-+5Fs%aH5STpm(b3>2Tubf>~#t z%92@FX0oRbsq4IyY$k4^>OeYfo~hcM zajp64m})ROP>^~3QIB2m%!b17^z5ybSJ8%O>sY=mu}kvM|1ym$r%tZ@_;lbuOaSmN zeoF#y25DjoL#qRWt=i|rh8tD#t(ChN4`JUlU8rJzS(w;rUC;r1pjbNR0>#_lV)XC{ zUweJUro29bt;VWdBz&4Ic=o6DgkLP<8vyf5YY}dBS3&J0p3%oPS z!Y>{c<6Rb-Xxfy!agmX5HEHm8-U!Scc2MG8mGFr!|CYPB;pMw89jHI@xpfP@&A)!F zGpV6g6Y1WlU!9xb5dg-HpLKcv#XIwe9D|X-RW8A^LlR)o!d4JpVpr@8z4&a*bY63` zQ7|aAYTeAt`HEkECYPRU>IESawu>GQS{;OL^VuM`q+QCKv&Cz${54UFfqHCrYLUr? zaO6kJ**wr|zTq2*n%9`Zrrxh}zc;E%%~@FO3UG1RtZ_ct9W^{&w>*N3>h3x2L;v1u z3KiR9oT|a+*Ok3XY|5j>YIPqhL$0SPU2&R1MJzwlc5UjW6jEYag>VFMU=mp8@t&Hv zh&LtpNDz~|Tn4{BUD;O6P3^s_rcxQ^;YyTLq5cI}uiR;`)U?%lHwT_=9UWb*&1RZgp@O^NVQ!Nzb>0A+9r8BNR&LC))>^2gV-xrxzC}3401$NVKj={P7 zB#PiP=OerlxbYAZ96$7vlvkjnAzk_9NfbCpd>QF-kx83sVfa841C|j?r=BWI$`w?6wzQc`Tq;RHxJ3iSIbo z@H0XSB*19K_k*=N0)oFZf#_>s>|CQ=iF>p}2m0776zxyBVEq`KVi&N;sE3VD*-Xuz z^8DW$LyQT&glzvEoC4hAK{j93vPBTI6ZP`H3#rVKBSnmQ!#}CGU4HYXhlXQpNW|Na09VsV0OWWVG0u6TWMg;!NF0a; zCoVq&#sN*)4KxG9ZX0N_1OBd=f}_jqtFX-#uPHzMjvjDgK^A|%cbBkXSkXw1a92Yb zNB~0Ei-6Z(MW6z)J|IRgh4y-WF8J zQ9XCT+)o=L{Wi$rOTX)4oJLo+V0{+sAO^&0IjJH4)z+%g+DgaC-Mu@}#8S~hk*524 z^$KRq@OPM-D+D#NwI0Ztr1v%XSbAd_ky?CKOAtDNo-y(ip6x>kdV<)5b;!P)GxTT# zf|K8!e@B#c3OGPUXLJfylaa{fbT~9h-bPJMumG9_V7{K_b2!EITw1$3|elJ z0FGBMhzmhi8gPZ-%~F2xidB_W7yT9LhtWw#C(V zP;23R)tf`{T6Gg@--iG57Vyu5Amc7z5eYCfTnw4lApzRQ)akL!g=j04_ujxuF19jB zW=v^3V+t~jLYJm89RtC(fm%L46W~=5}Olt)zj&3IUM6QBTKg$WVY} zd0ddumzucJ4B&;_-g3Y2q&YQ3P!!B`S=hlpWr>>ZQWXEk@;(j=?&MZP_4zCeqwj-J zIFJD!)5@}usG|@Sh@q9lL>ie7b+=G?swM(IGGyDd-e1-Ts?)v@NN0Hz2*w1u&sVw+ zTl2x{!5+ILfc%;#oCJ6WAp_x@JYzB*B?I56yuVmfqin}~_3}i8KNd}p$=f(FeS-n9 zPfc3lqjyK9O1)#Wq<_2pPkH|)0hTPtSl}OKp2lZzXAFIlOfxY5JE3M>~+0##G4G66mPG7LxOq?D#A1%7O;^W|N z&zy8_Gv&O-d6N_^!G)0ur`gec1| za0+fvW`0ND5~wS-n!f97Wm1=S9%-v-qC2%G_(?4r!UjB|{GP8>;IMmR(%qdX1AKEPty*TLr&a$|JEmw?`J|rcnUIb zVDkiP?9uxm=`X9cw2e6a!?#6NQ%&s!*OOmX=rW9GwSP(Up^IRiqyOMC;SGweS5JW9 zwI9{TuLu;9-@|)&`>Q`rR=6+q;DxVJKB}ei4`5f_F*Anj< z)EXBFP%C8i7n@JOYaaklc;rE+Ye0PZ-iF{btBt3wa(o(gqtlniW;qiXpinxInu{`` z4Igzp1$}lCzGK0&cG;*kmEjkxD@X`Giy7qi^IhO9D$$lo6pP;6E*#HEmk3S^yb&~( zsuoMMwr{rQO~HjOc!LLyuuUZvJ#=YZu%K!Afmc5e=d+~MgVkWB_J&%*$75Y1cR5oWe zNuMnjoTldY17D=pd)Zj05mCqfs1XQSaE!cCIgSH&zLEeKGGPCQP*Se+cF~>$h;T&@ zl@VkxKt6ne)~KtLs;)O06=Sd9HQa~Joi8r*{A@d=Vq7!O4}yjr#Yn?_ zan$1l37j7ss=_*zIHhX_upZOAe`OKdZJN{3~zh8v^y*{O9SF2BECCqU8@X6x!rHw+g#Qax|Yzt z7sdF#+f+ypgV>^=g3_ykF(Ar~^sqVR$Q?}9RiEn6Y4Mw$45;TCVi_N9#Vb5vPE#u6 zlPkQ|p;!iG+Y2T@XHIV&r=`s_4p_pwMx90GO?ufL?puz!v<=r!Ae-$3Yd2_b;lWzNTB5EcD|~DS)$L5yafMX6o z>_HOvX-VSc^?8;M9+aQYkR?8zIQ@&)Gr;XRmxQagldD_u)!dG>duS`6S5NL7>_pC8 z#?!lF`i1eniwu?%^}Dn%x#Q;RbL-FiFLBTvTDX7J(HXq(crQ%pTzx~rw8p(r0zHNV zxae#t(3HO}zy??+@-~EVj6I!!c$u+&61Je}s}$~wO#E@HGA@_~19^WCXF4&mffkFc zwsOi~{sy*Lg)sCkTN<_QEC{jRDs-FE`7RPEO4|JJjqg`~MS)%MzMpYqj4 zlUHl(ADN`doN~yTeg+xpzdTVsq}{B%*9DR8jbJa+uJJ!hmy+cdTOQXH3z$?+4A_=C=Nv@yYA7Kev)-x7Aypf3l~5Cq|v2c=~Y2=tjv!Y5t&K9CMf@ ze@Kg}61t(cUqj>WYmSY(!5$;MY@M{w=LX?7^6TE0dAwt;+ejNFURj$?Ymakjk-6(t zUzS`j?VQ}VntMJPqcd`|&>?YlV8keJ^^ZJd-pdilB8 z*A2mo>YRi-_`u@TxeN8f2lFhGczxpZz~F-#E-p)B(HRM=-sK{N@aWEF;|zMazXGsg-}kpSi~7}JDY=ebMcUJcy@;6jO{K%4PBX-NzsTR!lscO23$ zVe>HKqjF}dbr_TCyv10fNDe{l3@ROT*HwHqRb4q-PZKW26{5HFdR^VMJ^ov?fs{%9 zo`QIB8HW9O*SG?LrMh7j*i!qd!>b1ai)c}}X3zNYNaZW<4~mN`RJ(UpVE&+1C?(zs zb6%cb0K%?`CIJ}m10;aq2-o948Rw&i$j3I4%^gm0h%EV*_I8B@1;HB>HlsyiCIbfW zds?|v89?dtuw;kDBc)L@5zYJS^``b9@;mxo%OhOZOs?oDMJFFLRQ>m-9Y|5 z2eoK&>KKeL!tA_NoQAZJ0}Ec!1|K7*&Wg>fS155s2SP6vPJS85OEyY%gxTa@d6Kx< z{EJiZ0_v&viW8+6S~03oc43~LpoCKx!y-dBi9&u^j9W{P=kU>|pXR@we}=wK+h=fo z;fI6fPce=oHBUii;$^3KRyml)|~k*xS68TI#>b@hK&bXm-gy0Uw~pK=skkTyC2wQD~0`Co@?#IX{$0^hzv5Y z|8Aa@lW1!-q*|nKJ&tY;bt~Fu>B33GcUACWoOBDz8e>uQm-YIT=49szQOsW7bAJTr0JFREd4_u z9B-tLPtILeUN3!NPHYqS{Cv(O)IVNH^|SSdJLZ##p%2EfwXu!Vi_+5)ivkVkW0?0A z6X@I;Ic^5TCtPjhd7~r!_SjRbSAw>+)5d(Ep!M!OnqdKM+<0X4!4CVH!iSkIPjv6< z{%Or;W1;RtoHZpkgN{w|5yukdEc$)-o2LVugrW#Pby-$K&QKo|%b;!y&)0m`_YhjDDs+}Y zemKmIISa2|g+6<`$tzpCNwhrn8@!FGg}IoH7hl)Ut%)ZAGUgl14rXsUOu z>2X?~K)L3pB*c*bFBqjpjyj>|r5tMGowkS^zpnUlUvJ)?QBb4fa~MjT8M9@<{!T*= zhLQl3S4Z|w1F1mozpYCy(nq-Tw%aAn6#fFe?s|Bt5Is2EKz0!cM0xXyd*^&YqPJ6h zQjr9~sa&^3i=3|``lu`5eZ3{$W4+rs4cCp@ii7SYsocI)n*4ptjW+JVlB@s)-x87; z4dz}$60e||*cziO(odyp%^u$*zlymmJP#2oBWyZv{>1Xpb zoc}{--P?Ahs}Q#$_eqh5_PPGt@KWLu5CazP6#O1nIdchQB$4__DT}Yc*c=t zLjQ0)v^>A<1!+YH4YosgLQ1dYM(^vt`d8K2Qgw8B6oJf|usA|-)lPW}S z84ds#kerq68S&?T-=hHz`5e`pj%~Y1sqAOR$X}4LG$3j_yB+3%PQi;^#UbD8NW@C+Z z7@_={ysK8XKGwb|UH*||xZoF*#A1=+*;OV?Hckq)E=sR85 z6@yr%BR_7vL7c?}1#_De4@Y`mf@R6YOl?g#egg4Us+7VwmrY)7eTcs;+CcDYAcm%b@w{YMIGn%*8Di}v zI}YTOhn)T2+0M^z?yJAxEYDXivI8z&-+qbgvSkJH_>lnUosOMIOZRsL$slB-6^36y zn!PLVWlic$J%90m(av4E*kYc0G3@%VPc6@nqt7h8)bb70_84);GB#CT%ZPxZ7JF+!)7~b~QRle{F6fH|Y#?eT;a1 zLqKd;9q8xtOs*Hit<7JM0+A?vlXfafZzm+4bca>w94*yW_od6@!Md%@^7zprKOqoK z?5zV*mU$L5$P58Z~;|GMI%v=@e4T9bf??8OF{sad28yM2W2NPDx? zU`}`?kH(0O!B~F<(o7&9s~d6$Eh?9WKU8%@GOFkZWRL7|PGxhp=?ICZJjRPII*v(}FxKB>ABSdlCHmQ-y1+Ev z%$$zt;83@qr>>=qC z;cR^B5Qv_#YCSoMy%F@t`)ggT3q*9fbw*f|AJl3y`HT{OWZrA!E=6%;jrUj%iw>u+oDvL~6><@>WZD}J- z6xpPX`*O;2{n}w^GS_JDTTF5Lh;#M5Tp)4kcEA?iy`biWVm#3#nSJ5q*h<9?G zFrE_c+;q3ce6>!dTW%7Yr~2z4LBPlJsbg<9DX!9J+=z=9(BnUhtdVv*kM)Br9RWv zM)>lUfX0B~VbQv{kV*D|q&t;_?#fGr&)$7#XpOMve)@TFCtO_~-&N{7N1o?*ukcnz zqfbZD?hV09CiOaIQZ2*4-BJS6IyrG7j*D?O2ejyK@?ALL`m;I6rE8T#)XM`4olRd` z%%HmXzGzB+x}*@e5D)^FfhpgLt+Te-&d{?!4eF&CE^`=IRF5Y)Zy0Bh0E#1hSaOP1 ztP8=@=!%IcL7WFI#PJ$n&*z~_bl@c@B{%Bj_V=DwEqiaWES#f#aSHqJHFO*?p-NkLLMB zNaRzG^@8Opgx(eh2~Z<6(9G{_t6qzp3txx>p=YLDoK9YimJ>9jiX6@|E=LPWKCga| zwGzB}@m=brb4AY{h_n1C4~Jf8v~gb`$KU|`hj5SzSG18>e4c$A~9&E4R+jX(QZFLwUj0g%RN z>**IiS&gAhOS8=BwMvmjEp3&o!(58G1mOcQm>IlyX<6LEZ5zzcEY|C@KlN?8w}IIK z^fk4|ty(vIVo%@3y<%~`nx@SP%IZg6SItI3c<}Ly9fdl9j6$7r7jXBL_1Xi8?ovm= z?Npsf#W#)kM;66ictpx}LC+H)W##YFUCa+)4{<^K%_rPX!=;uhns3cNDDG3Bwr-~7 z9$;td2xj^7u>Tw)jCfLVI5_kz<%rx~zQ-<2wR|~rpsXJ7cmSl$XZ1DxLjUuim$(|l zArSv@U7d4uf5R$49_as?9SOVYI)Hv7r!5TdeWlN&D!@#B+U)AVaxS(--tDZMr+tR~ zQFCgKQ2xODNzp$8@;>NzYH+T+srh-xo3F<&wwLO?ZIpn= z)ObR(JsyD#>>z`0uDC5YH1Y_+62AW+NKBqsdl9>xr1#s){-2_Z zC9c!Bdt&L)$mk$Rj3l!Gvf3_R7|1NA#_FwjvwwNss>kC+Z0Ye$Ep+ zzr#VtN797z;3X&WI)3YPxXMBqNcc$3C2U?qoZKG$%XHxFv?tO!lGoF-GC33B?`upV zAhNuOJsVl@epyM(Z-_d#fSQi?pw4kbm()TX#=LW z_bAlif4REIuj^+BH$E!A;4=onw5BdPYG|N5wZSQ4(^?6*^D5~&Md%CePJeYc4j*&F z#^r#ls~$U|b~y-dK&(e>5DwLK@#BlqY|h!7_#L#QyD7G;Nh$^1q;7FgPQSWq-cQ|L z%bHL*Lnl?HwXbQxVID?Rhk83WqRxqT$M7?oRy8-_OE5bZr`zd*WewrOBXci{05Weu z(Rah{9^3d+Dsj_zjH03M$PaYqd(gb#3y!IC?1>hF_agPfZ&+Bw+`LIWdH0#lrKLnF$r*?f#d>=){lAmEGlp;dzIHQ7#*IXogQ{<*@cF14vtUY9*>^AsWr ztz9^$tLuYH(_z)OtXhJwMcLOfixN3-btchQF7vU*WQWz2$0C@>V9+f{9B1|xSPiU1 z)XE*d44GXzp3^$$Jj+P)QB!1wiO7xLMpQvM4b#@0^LBcgYY4319CUi*TY}I6_`N#2 zd&FUC&EpLs=eV19%}DfdKvw$CIQKv=?US{jHGsvu)STB$W{pw;13?iR0$o5%jouxr zZn#a5#WXnDBRkri*}!9LAPY&MX3vCb`=iP((S?4VxJH0_A$G70MCT{t4cwGwaI9BZ zhy8YzE?mQ+GFDck6*>f-s$`@N&Z)y)jIr90@`D#hfL7UWt30+jXvFo)#IE|vcq^bJ z?X_&(>w3i|uVmhkJr-O8Kk{8B@Ii?)u;1kcy{U2;OYXX0oB{aMrMW=`T(m2SC|nC* zj#kxP(7Vz06#E>%qCM)Zr#{|is^F?t(&PCo=Z^W-@(+cp56o!w#-g=%E}5FO3o_%S z(Sz~os#AWp^!!}fqh8GPgTaMs>lM>$1M>7yd^aBYv?_3){48Bovd+`&#oX+=0+lK)fN&zE<@C| zM%^1$z|-0t$aGK#h`S#hLtfh7rRn00zRi58I9u+oaQXJ@K{2h$_tM_(9?R9B*I3BH zKmsSBG54x{R-Ge`FO6eTV(-CB_zM-k| z*ui)EK&DcctCvT-CSC89q&QIa8iaOSOq?lR`SOh*i!EtUryrA-*kSeq-WhU-tHx|( z$vz63)-6ry>VN(ioCNC6)+zIsTRpwB_7%QSl6=~4dFeP`VMgHjx?9cI=dQ%FH7s%7Txo~!PCZ2n5C z`_w52>29%aqupgDI1PRo_wpUmv*7ECnp8Poo48k7^rDAmhQ_H3o(Em;RO!&=k<(8{ z8zs?O5^a`&N*|^j4-G8un;*C)e&ass@YfCLiwShdSv@I$+n4w`XyN4Nn%oO!ohbT- zth;&u-od10>acPc(ynWd}txqm_NkrlTG z=u3I)d?fMm*%$acm0w1iCEpz9u&SiJHyS~2Guc}IoKB>pyQwa0GT}z|r8}_8dczTq zFZoBR@8lDdu-TDllpdu%wmGhFRlzfVye7@1u#LY|RyfQ!@v6y%GGU4~iVtnRwoa-} z*!fm4tE4Ne(F*O#)uAvkU?0Zs0*b(0^|)zqKe|Q#c6X6?nwUs^6J%?$BX|NL=5io0 zbvh8Di%o{a9qb{yk2MyWGYSb-$VTojLHDEQJLXtoG{@b7B%R6yYpm7{G>du|%%m#A zs=8Cb_7Xyzu2og9eBv%3HlhyqV#vNkQ5;n;NLU@Ye0m51r2g=aM>hi(hrb zZ#sy{A0T6z1@LB=^T|wUm;{DZqVVXI54s$wekixzw zLVqSG1)}*c?6r*!xY>`m$xYgh=;-d;lGF6zl-_d_Qd)tDSV+vnNC0kIV$JBuP>Q;v z$8q7BBiw#D7*YZiwqCD?M5$l%L+Ji!h}daSMs}crC2UYqu4No@mr^8OTnT+1_yZl4 z_c9XLaCuuiU?(1WK7`G@f5C?NA+|Qc<|%F>wKkpamF5jjUFGlgRz@FdEmGxQ7~f~* zm}yCn-*-i{L1?G(A*c7=woczr=HJ()p8G72bpd6qRdM5C(tR$MYjG@q%c0*&hA514 z@QMrc5So7UwMIttk46c(Tz@aQ5y_62?n^g)LPcArOq7I_9tRp7_B_mpLrRMcrlq)$ z0HH*ivR@xOjdZuJOY4()M!W89OU0#qxxt^wl(Au}I7)36i${JcQy z32?keESW9rj)c&;R}Yl4IkE1cjl8FtL3TrpK*qASIb91jz*CU^@S^cskhBO6BE^3E z%#PdQf_QA&++8W16GITqs;tHOM3Q5G%e3>NTOOsQmf^ntvm1|ov_l(AKp8;sH$%^U z%(^V3CKTX#Ery8vYqs22>chMjUS#%wz{qgX}`kJlEg|=FqC4Zg0Wk5FHK&N zdV2o&YDBY{+40%g^)i>O+Fv8T>-PeK(td%a=GG5)79e55jrR@Cl3&)0P)4TD+|T8H zem9XXHp3#%^i}M`$XAMjnx%vE_z+~PI?srg%Ei{+N7d247-xfi3|(EBzaXJs)|m3J zGSkO3bL`oozKnCfNbZ!sv@n^A_T(k`1MuI?_#eq@{|lRKZFlEL`42YR?|;r_qe#G% zb2+KgjdbS0Y|f(lx-0w(6d|8hC@;`$x+Ico(GhrZ4Nn6=#)K*fM8(I1AQB+a{SFE6 z{VahPTOz+U+%}RWuQaz)MV{#ao|HoFAD#2aJ_mxFV%yHiB^n~d3-id5cf{{pX6aN$e((!(-+G5r z&zra;DOy>p_Whgg_mf(|aNl`4$Wof0F0sXQ{zTFx`9|wEw5{ZhqCJtLc5eHON3q@1 z1GZ_}6C!3s<~w|S_TTRL&;E0OOvhnzY6^j;$Kg$1>6>-TS=j?{W)h$AaTsbWGTcpS zn(eleP^6@a33snlN7j+jWnQN-#6w4t6J+)nQcLT*{P{a^-sLil86EzP#l; zCN10zUiDS8v~@Z(G3Vyi(>*C)#&)}k9cws8*Os$|>q;R(aPRh6H6gwhj88^G>HFC& zv&*TglggF=vP+Z+=<~37KcvQ}7kJAmPM>yDLFl+{@K-yvini*KPnke;$V5SdKg0}) zWp-85N8ZIvyuk$ei;a3ekh&UoD9dWZ82UPbvZ6S^y+fK@4=6u$EvRCst$Vt>Wq(>X zC`>VVRAw1%J5i9~Pp)jyfZEln_m!IhF}~phS-;f!)C%gxol7ak=Yxub9mHwZ8Eqz1 zU2;~HUo|(6Ekq#2Rr|I#M#Fo~BqDR6@S?IgOGwLq4rEaSR~CuU74j`~er0VHdA%_U zdb3lDm*z(?k05y<_@w=5r|CXLE(-ch@t)n-=gXn|9Ufx_Th30k*-)$(K?4QSC>keT z_cOaY7Sa-2+QvA+ejI2~zrTD}5N3mHeEkLgqF)LwX-MxUaF_MF$Zk7Z>@~43^6kUg ze;tP&c^suqz-iO;OxCHV37@kNzDf%Vhib*yZ*j710%AXs7!}RKFy^Cy*BL%pzsda0 z-|yk|594UY=c8>DEs4K=OgxPz0RqHQ;0b*{Ie0_6l=#;t5Q{s^c-u?)V+ZDvNv7iO z?04@BV&7WZvp2!?e0E2POo-4xr;UmkD?C!8eweA8vyjpWqwuLeGpHo1%W0)z%cK#A z<82GM=AZUxe4)?){nti8h&ld#c%m!Bin+2@K?49-6CJj$qy|K z1K}v6P$!~!#=syM1iFJqaJU0By9QVWp$5F1Fw%l2Q{o++0UxCYibXE z3(T1*XF#HDIqD|{5;?2rozH2#YlBDj)y*if#CJ96loNEYgWi7z0PbCi zF0vj{$a=6+GdTk;MAl^0^LHG@d8sLC632gxEyTAy17>6rO79p|dk$zjC)~rWL=tpe zBnPsJQG*P66|+%~_#E(|n=Ua0dV*ODQJHRrbtlmw)4pr&8+~exWdXriTg`bc{QX~X zHdYT5g@=B4Jzb|=7&{1Gu%T|0Me|GOI)%4c-MQFZQmq&QjdMd<^S;)9*tZ$uD%u)- zt>jt!aDTp9p<$l8Cg1`&Rqiw=D+Keb8V2Xxc z*_Fgf2lcLv8{^e4D;Qq^ZRl{N>2(sixq6-1O}R2jmlZ zxkg)}AVZ$y;>OQI@`=E|9&TRCJC&s&?@AjAepbH62l=x=+J8Q<31^4ActQfKOf#37 z5bem+v2Yhx65wvXFS(9{B9J$9{pDEVdv3yJpCIA%?m;x7?;q9h`lKd*A&c4Y$LLO@ zec4@KHk&bBP0WuhsCDJgGtgg9SZne+gS6#1RDr&=nn zO{CSwra$L0dg1cP*FTDN_jNTZ-EDrAbm&iD!-)H3e#xD(s{|-;;>++?;>0*y(mHEc zHaK;X1Q?Z5O+0uBZEbWs)fp;$$UNs#R-(6E22I@v9)NRf3D0%6lOK}v$YVZZI@vip z`wut*X*D3b3o$a-mq`2rLTx$1h_Lr0Kp~jS@g-D`rl$Er7IevA>~=Z+p*nd&hjz#| zGLIsIG;M;2ATyc9>5GKyZ2f_a$|lHQ=zm_TPa%5#K_NC4eLVi;;m1 zw*0eE^gD4Q9oN%3$nGfJ(^n(D&SoS4E4jS(0P>f!wF%PFRFENZ0a3((%(xYImH@$C zK=*c`MEW!mK$2_xW>Y@7_#jQZA4%j&3j?l_OFc;_ic{XcmfGt6i1Xv(y1-hxZ2c_Aq0r;BEnCx6jH{A*Z27!MaXo~&WvhF0+5 zg&J~@aj~wA1}<5QlK>k7?q>j`5jKse53^pM7U{{ES;r!u&nS&fBx~3;A_V*WG0OhB4@j?$$t8n z!7afTC}E?WDa7{emv%yBG8QI?s+5%3*!3W$uIprUO1JWFsqMejtNprRbNA)IS>dIP zOyiF|Uw5DScKQ^(45kM7K$uVl5Gu$15`VS&|+S-a;a45`oGxL@RygfB*A@EV1<4JC`gdur#{BrJ}wZ=5ET)RqsC}JyF z&$&rW&j0vLvz-!Aii-du0Uji7+RyLqjUH$^9$c3TR%}njLb{et64C%W&BoS(ZOM2u zor1}({I$cwL`X^aIp!cH_<-fwEo;A%ljXfbM-CB#$!jGLktLLrFhg zTMd)REgwmM{jb0ZAQsX6AFi-@Y}ZFrhjiC|flP*wqqdewIt;f*)NG<8wjBabZj~b8 zkwmIs@Tz|bBAR$tlijqF{PAy>PtH61opsi^vqQS8O33V(%=r_FvO=^pA!&yWGAT~p zNv;X=#2~_&AR0kHoM6txuqMZ1_WS>0Zd3G;*`aR=35vs;-je#fMGKANH@x0Y44Zt? z^Y!Gfe&l;AE@#=WO9{Yt&QDF=Z*YqoCxl6WHt?Z}A35HEI8*ixn{x1;rg#oqBu1g~ zgJP#7o9)Eq3glM`xU1AQZx7qU>e>)nVZZ*eNjCaxnafuF&yXAeL38dCJw{XB6CkY3 z!)3RJ60qi@{)Vk>sOKX)TB!a2&#r{EH z^x%6oubm{OW5dbk8`7%bX7np%`uE$G^RUO^9jc_v3n;){)Mc@4^+U%I)CTmyBDJ+{8Ej{;qJE)4hLdH#TNC63)=HchO zAT^wR{#Og8?5Hw-p@5n>oe@y422hyQ*NIpqpQinpG3Y9GDlD_oXaQR ze43M2`hDLRRZfGv(%p7WCk^U>BR*O0r!At5yL|x3`?R@9C8}JCq0w<)~V}HZ#Px>v75^0id+saAr zDQ_k}Z&o_XsfJQ-qz^!uBZD+B=0QLPGxaNop@pyqUXEXF)<@|?b%fn-a5MO%LxVzJ=c@^m8}oF^xl?d3IUG26(t|`4G9SoEWo`Q&|Rn`(qgR zzGkrA+EwUmy(m5(8+4Zhpi65->AAO>xduBFNy28~<3&RKwLV(21GLo4m-jYoB~K;3 zLKzO!G&FGGt!wH1wV|rAzKSU8vMaUlRNjcW+4~-E5vL7fnQj~-1gNI#+J9(LgMvbmRg}jMW zG^^YtJtU*l&#JwdRvDgId!jiQCAo!3Fo=+67HwhQm6NLY@LM80TCbrQoY=Zc0tgc6 zHvS-V!Yn8K6?}No-gv9g$xDPKu$v`YTQxq!Emd7-sSv(?iOTyjUjnC}VCQ_b$x3*s zs_sH>Lga+HVAzREZ$tmRkxiSq!KZ3TlAmf*rg98k-w6M0ZoT!Myw7_|60UiBLpZ2OikTL*)3WEG{Er-AWzE0kG;Jb|9Z2jmJG&dUN5F zdgfvN#J!5XJyc02eTj7k?QQxGo5f>|il+~qvM)~%q+NmU8bLX$bK;4)J=f&m$fuV6u7fT$pk^NS)bb1DkAGjXVFgq)jop(FTKH zvvQt}YYmR})}rfMb8De3A!osyC4x!q{+)M=k4H_O7l9KCk9@Nk;o( zJe#P+Qk>N#k<4%EpP9Fm(5K~eYbPl+Z!x^E5E8%?O0M3pSZSP<9IkS|!+yW@{-Rd# z^IO%ek{J&?>+UW!a|Vgvz!8L}*y&S)k>){{yfzHHIeCjk@uR!^r5A9tb?Q8iY)~C4 zR)Lu#&JGLhvZcp8&%RxSMIO#}W-JD~81V2ZrVh`{JJk+rUQC}|dd>4IpYmnO;;%^R zH8 zww5k3mr;zGy79eBeulP#Z|0bd&d?@G*s!~JoATr2=`HwtctCj^nWp^SRv4Gonvat# zkCFDmYlopsYGaJx74WD;F^hN2cN$o`x6fH$Hf~{a=hYX;!KPuP^2vh@@0@9^~&^v^VfQZyU zC;>u|o=^iM@vi;ebKY-n_gnV)-S3Zczwi8^tYju@%{8*-m}5NS8DmHdYY#g=AU1E= z9-s|L3f>bdyP6T;Mi3mLYf*!%&fpH3SBmSj@|iy!y%8jz=@ZzpE#ag3(dlW!jH0de zS>bjkWd#QndK8wg6)Qui$%OC#mM=Qq0p&W8lY~x*e0?>Qzgkb7@dB@g@S;x~`0|Gu zA&?7Rx(+LhIJ3G#Fl#THAjFKQiKOMfS$tzQv#5Xn3O!ij{Up`=%+!s@2B+}A85yit zF!>rOt7?m3l_Nh44CS;(S)Lndyqa{>*h@62_9u1^ z__vYjil!zm_;P^c*nV@0l9i(d3AN0LSv*1y7~~2@`CH+%I?@?n9FT9=E@`Mxd81jX zT;;JHqb{cA zFydUo_y}2B>8G-*daAfiz*2ZB+nvJze(xozMyB)iQy#{_b5a|Td& z9T`s5e%gBLb9V6N?n`c!eM=Jj(aYW3EydYByral@$0{rsx1*JTt3qsf$q5X6fg^7q z$O@v`^&mg{rhWv$G~>bxQlX^E8PE?yG8ax{EV;1XVc5Le)vut(Y5$e^txq?uV+!@+cdiHI5tzUIfOEKw% zz_^*~)a8V?XE=TM_@sycpum?Lja=tP9$gb&*a1hL4LAhwfSdu~F@F`navD{LvN_Do zG9G0gr!!w58M!P2QW!F45EDs^fnnk&nB`1>)Mp2nA%i8h+uDeb8y7e2p!(kRjc-okqZYO`nWyv~^P>mH z9eP&%`*NVuET-hlB4c|b6&CMZ91q{dIrn9UVhU{o#E>YU(mXuCwj2L9v97 zo7xgDOw^#gMB`<98S@UTmDUO{4>|YKM=d3;j}rnFvN>?ZS5s-M@tqKvP_iH-qg#b; z9zRl#?7oa=)T@$@j zylq#;1x2+_L@|`-2F*NO8Ucp|YcT}%IoU== z+?M*#>`0Y`p;V2D`nRe8fyM4`l+3s8X)&|6u+KiFqw2^Cab4`$c=!>|D6^B_=3n& z(BTRQefy9-l95Q`oL?1udWM}SE*a17cY5>%V|2&)=d&;B%0R7_NFa(#i2Z(qjyNnL zUr<38kt1pVRY?>O1eREY${Z!B5c#9XA6PVP`Y`f61n;hHt{ox;`RhRTpTA6TWcb%k z`iVYE%9&Lc1)mRkTv|2qGy+m$9kI!rsBbrQpXt?2-dplT!k$#BY*)$<&1?{v$N0qj z$Sp;I%xOY-|0yr@?vayNz`0i~*L941qa@B#5;eyf)&4x4Ki$hGdPh8{E#nkn$8fkx z_aZ^zHl8Iv@2wNU)A<%n0sP+6_Ff;Ra{m^dcQG3FDVR2ulZ52X3xs4N94O2a#Gu9+ z$BhWcRh2?(hH|pJxHI?i>rJ|w-6N<-KvvYp2CzXx0t`Tda{y_^X%cy1RYoLcFrc{z z4*+`9Y0daI4Lk|F2?=k?O^LncDj#B4PcMU{q4i@B38Bj1oux>MD4HJ{npoh8UtdydA zyjsd=g*86a^@;b{CMj3Z2SYCa?kl;-uK>USEV*zqx&G&-E$um;uvD0zoO~09y>0BW z9PhK#2>J_R&#A1cx8LSOxldK9gw8PF8sn6s0dCTLNXd)F=wgo_R@W5UorVGnZ*I!I zSZyzmqD8hLsZ0oHFuUp;?P+?}ujl&a&+L9-A>V#&j7t1Hiuxi zp?rCykv5Ca-C@cEcF*Cu(|gwC%7>S$rP@~#7+KZZPu{$(RMZXo3k!oKsYdlZh~_7S zqu9joeqVj*U=>{EzVh|5T5tsf++A;jM9*c&Dr>&yV&87!1E5Z^>%uEMTvPVd&Fr<# zdREd6aZj|l_hyVwVX4!`TQ@-85Z%#Y26)tl6jp(Y@e3Z>-~-nND&QPB#dX3wI2rGQ zDQul2Ur+L;O-$XSqh8fqcf@J#rwo#Ls`5LxSTw=2o%a?iab!3-qv)<)&{aX&2H69m$k*_0g4Y*Gq1nxP@_qqC1yq1 z5QIzlu?^DMxiwMGl#agD@wRM$o0*#8;iqSHG-s80XAo`lq4N2WRcWk7c$=i`c`&2H zK8gyF{YYQ8bn@O@W1?a0liuy33p7!q8lr|ZPmpG$sd8j~ib!|T0St$RddFfG21-67 zK1zn5nyHCi-~BJ7i;NMC=2Teh4h{_QkQm&Ir9H|klkHut)sm!3d;m%$)I9J+=W_yU zmpo~i$k?^qk+7EXj(mliX~abXAutu~C7`D~p*Zio$;Rfi@KirBTYJPzj64C`C@%Dv zTGhz#1~+4t0<_GxI&``1GtMj0!y!c`J29!bW>Q}ZTrzVLxGqZZpIis_qo{Z}M5_Ot z8A1F&huAAij<7D@{g6CIC|zUy*z;krnQNQ->MvSrcEV|-6$hG`7tk%$4k~EBPBeYc zUTiRkMk>wBsA*6coibD7Eh)p1yHM@*TvGUEIJd@*i(;vni?Z4mxQ%Yo1KLgWajG`4 zcslBH&yxrCAT830WV3hKF1QU7ZfevyK)Et=Z7{pOrlN6&X%ciLx3e*>L6cv&QD2!`z~14{{^aqN=n`OMH3o&3GfTq6qRQ?B|t zHm!+P9S5F6MM5PZ-d*w~6?ok)j>i*(eADtlzQH|>aA$pmo;#JZ3j;yh#V-C;(M(jA zQa)(Hi{Y@&tBvcsJ7wmL@VtGm<6%b}rn(!&WYk&a{}8|2iKHbD6tG5!`pdxAIL6@F zYeq+A!CG<7RA2Rb%Sdhg3(4BOy)jQ-dA+ub&753=Q3AEEKxZJzVk~Pj!Z<`!Br$bTuD^*DOIarI0Zu0^dxlaZH+L&EH_0x$2VJsu^Ze~iih*3Sf2f`s zfQmic$&P%(A$10^e|2LWsaP=;UPAm?w*!Hwf3OL>QclcjcKC-w|6qR*qWr)4s0JVU|RG3F%Sez9Oz1C{B#( zH3d(eG8G|YE+c$~s|M95fUqZD8C*V2PVsE(=k_b$B^E0bv~qR6oZ$gN%za&${fi(H zQz8L=%s}KMYU5-s0wDD*k)?g9rgkIOXNK4=x(ev5N}CN&nQ$n z3D}gnw|^gMX~Qo^IRVsLK})$5A%tVk|_!#1BYf zk_a(sRZio{h;I#ym?+kzwcwYRe5?sEGa-HQ)D7By>!43a)*gmcmbi52k;07*o_8Xl zSYAerdlOMIrhD>)u-TF{PXN9~)3nYAjq%1w4O}D$n&Wk{83XqGA}8(j#g1+16EEv{*_I_HbT(^SN7{U{*VBQItsT$Zns-6@q%s%4d_P3#m$}f{!xQKB!5!2g;8= zNQsv1H8oKNTiSQ)5~)bj8<1!npW1FSr1x+VAn3ze$0Xg7qIg0TUP6l?gWEF0r&m!e zjwR>Z5lrVirJ44w)LiI=f#41KQp9_rFy5~dS-FlmqbNdZ@|#U{_HIKd^W$lk!)z{C zOjSzcmIj{HF=eu+krUo|N^xdkh&+U$_cy@}>^~!nT78Dp?{qfPBxu*9YRXTyPpwwF z`4nD_xE~QKEwRmXLo4?EMTjIRmOuXUMiEh<_a_C@#>$FszTT6?MNfGi#|eYOAT2*2nB~J&=QA z3_9n6mad-VG?QZ!3>q0lca|6qHiB+x17X*DpR)d=hIp~C%AxR)ASuWNxyuhn zw*k@ocw`oATzc&%#hnLponTFJ^W&csLvP6dEd)s1;T!pp6>(g#82f)7Dd%>_9nY>q zsqwZQX)m1bd*1W6Ey(+{@9ex7X=CQ`s>{r0=fv5MbG}%vwf?a6jCpObwgbyk-=QK7 zDJtzWulZHRVG}R40-PN$3v7I9zHv@-=T`J!!}X7+=bK}*D)wE=4F~y~wX(N%M06f( zDAybwSTt)!6WaHE)3r6*js^f&cK-%rK)4i*S>&aH3j>tpo9(<$dskZ$ zdioz#l9G9mIB-W1kY1X#)uGH!cx@*l740L!S9LD@Yy@*CRc}@o+gr9W{*Pq{_pFbZ z>;=~x`~8S}(By!?Tdp-?T2M~k$$|29H_Z7)83aMIHHkwrTv44hqDsMF`&&L;`i;AB zF&Ef^YEKDKy!BC>^(e`U5Z0JVn@D+Z{R^OU)P8R<{+GO2PnAD#MKhR*%xg!Szyg0m zqN|P&LR{Lt^Lq^B*W+@3lg#_`u{5joil&KUF$eoYrhwsK%*@I%H&OtJJW2WLzP>8O z>@1cUu1?|O-Lf^2Jcn3c&L#!5Kv^n=_&v_LelFbsyTI0RHj=Hv#;bUH5G7z-u63#=)OqOr&ZT7kOqb%UtWF(E3Vr&b~$vs-Z?sy2ci!7Gf zZF$3tWmrVu_>>omq4d|*9AoFv_n~gt?iFRcCt6A_f+wiWb;FL9+f&6Nd_(fvBgaKE zD+iI~R*tE9()$cjW3xkdN)E@FjbGP5X#>R6uO23*Pr^Wc$C#Hw}dgQQ7rvwR}` zF?BSVlMr_72qRubQ+0*ygUhWqpbln;)CSllej#8=+S`+D=^&WQkVXj{J0yND6BASO9fELfM!75;{h++jGg4?9-=+?zP{c2CY_c>bZ zITy=3uP=VQI2X$4PT7hu!_l{LJ-(0sf$xqc+Q`Hx>VaPzK{Kwb;KxnI%^HO7U3txp zge)4lSH$0WbyF5$nw65E-3+fe$jO)^*j?R>|B;Af0g}Igc-N`F2iGRp4W-2nDERT| z*G49+FLwQaoIgYSLHS)##3z51n`?N}1D-LlefPYw`3tGZ^_=tW>7zx)WQOnaI8>;| z^sTep#z0gJaaD$!h+Q*d{_z-jWohaUQMKPM@V_79x58oNh*wZaf2|=!YwWbLE^~FH z^S-l~`UsC#s)LqG{Npoh@tO*%J2awvsx^Fq<946UIIVB&uHAEX=8YK}V`At$FT}ub zyTw4Sg~E$@5&8$yY0Y4v`44aW1H(uDflyYN((fBhP<|j}&LRmWJhVi;Io)cQEzEf( znZtWh5h2sFWBnXc;kMbej^&eG1;UwEp*_+W7n`|=(zuQp{#4!MwrjLG2j5MfuNo@f z4qP2g{>sVXK`AIwLm7(rBvcf&NNYW6d*MeK!Y?RSprGC^${a9`P1Lr-A;lb9poWT}Kw^d+! zkgrow^Z4eo{`U340ThMxPI@lPm_y@OqHXhVFBOat2H*Tf~aty<7Q;iBE8_4qAFZ;|9Y(gW1k1df;*&{OXar z+gN$@T4h*!_D<>YqFm=ocW5v9+VE~PxXqG_P~n$FlpS5DyY)lguGGQ;-TCtCuo~EF zWBalw0mGR{9n=}XuCA+4Bc{89+96`Rq)t_tIuq=Wv|{M#xWqZ#^DAVaW^vpc=_*gf zimySApwRy1uG310LF9f?cdru^jSH|&H-3#jNkvR z@P3Ec|I_z=F;1udV7r!FGTeo&Evk^N(XZ6 zQZmMC5+mi2B%gkOg3PfB1yo3nC@z^42uJRkqS6VmT~MBkc65>D?=eQgPwunz;Cm z%oa+;lwGl30#1?IdPZAETx?!xnl9bHX_Y8n)nC`tudmI+I-Rd20JP%jAnQ`;Evk$W_v{otv3e?(gF1FR5ibqSarSt6pcZv99|*WXpM>wuf84H+Ne;%2x5|;MazbJ`V%4Cd1y=HuD*?Nrm$v zDF*tQh^sj%+cRj)Kni3&D0W8SRpai$;MZ3v5usy8Gh_*YO1!xS-O!-yQ~Es3ktc>n zogoV|4_T{s`hHR_{}49E9KObx>M9k4%>5Qcb9&f zhpxIB^J3!|#x4f3P}8dHP?Pm!Q_!R4nY<{&XWjy8_a&K8a7seJF zYkKa#XsR)bZuL(}R9o0(`{u6OI3nVK%UfOIe2Ve@_Vz0DgQUCYoLeXJnZ_DNP-Qq) z`q$!BH^#f)Sqy423Z8Wx13Ckp7hU< z@?IWh|7MR$NlQ5GEmhWovt*T8{CE+%7RbJiPv@*Ga!Cp}{yLUr&Ww^>= zOgDJf&w{$$*E1wR0^NG9*lC^Zt0}8E@uJelDmlNq4bac) zcEz!Thmt%;v@1vgD-U512=avn0qH(XK>osSjj}lAkH&b&P_ZWNjEaoy3d@KufLd=? z1s3~EDvW{s!yq_Z8+NGtyF%gM7hcs_x(V zZ&`f;qRC)wAV&$HcXq|Xt$;+M`Qz_?azN!qsUQDtF3MFvq%$LOOLcvjSd7XlSDCx{ z9ri;e56HPPhha5|S2^fOr*|<#npmRUe@vVDe`fvvhOGZ!PZS{kAa|3at8nWge~ZDC z-t8IuW9_>0W7|a`3JE2&Zblt?1=q!choM7UMmxMO-&skcCSY-Em3r#XVE+%gPX^be z`al&9hSKPe=2^1#i12k6E;=@bbnWM`wi;j;Id>#RrU7u7dAlsQj-KJeWVWX7zm5QT zGT=F}Bj#NYkre>0ItJB^stSl-&EwXABs?O(XSmZG31mePU_h6tIWvM5IV6A;2rL4| zD_&9$NDn1`mE#-#)5xz^$%=J+eE8l?v-H=2KVP1EEPvR$$cEJHG|H)n#>e^Rvis}QxBfUH)S1l_%T`d>%>BsQUJ z^a%OIrT=yU9ovH!OG^)$O{#GYdS6ywWAYEg-6c<3VQ0gH(aJ`n3Jtibb|8i#Cy0u}T%KC9KiR={^!C$>PgS_2Fq;CH zo!O6if7as<D;Ya(RdtJuM@W9ZRa((zNf`?04U%ZQo>G5Jry^FHJxVD)3@UDVa zkalVH@a&73LDROfYtJaA?!1zCCAZIPrVGp>XC|PeTYY%Fq*OBl?05-00D6CZN1dt8 zIxm$b(TnzRn<;mVo2V2Qt1hw@&DdXG6{CrkKVK?%DW$5=Zl7biv}VK!zO~U#y2O!^Ce07MPOJxgM(Y=F(cc5=e3 z)v79Cz3grg%UF+9>G`J%%Q0>aw1VyO%0c|`_9@yiY5YNsRk`JNjeReEdJ%DLyIWhD zE7>XmqNP?l5}GXd5zn}_Czv8>yRiB<}`?>h;JJy%{ zng9CLX;Z=B>|MH`$LdUn)*c(B>kSNI!Ie@Y$Vwyi;jT>N*JIN7 z>Ym$nzddIIkN&-$tcc#+>z;{)>QYN3=C(qPtj~b(YNG_8*_RRkdZjoF4_Fpda~Vgh zk{QGkfU*ZP>+(?&D>*=O6SM%TE+P&9Ir;zY8_KyMfN`OBoP0?AKi~K_(*6I7tfU-u zaV3#C<^fsV_e#q%sQf4Wo=2DZM!ilEDvV*IKb_0rs_k=Gew3pk;kX|Fh3mh@{+z%) zKoZ@WUPLI@ne_qBACF=F@&R&3F_p|57K8lZ3xp9{uLH(MugaV;WQ`nFq(Z!i_(^dI zVUiVFVz~X=^EX;>NZKL}Tm>u$D=gxO8^}xi+ev{{Z z#^yz`>Yf7>=J%6Aq6hiM(*mZ%A1}L?B6PF}7OR834KU5fm;sVox52-j7XSzUc;!Fc zj%-F~`iUl-?AY@WDdRmyIFQ_DX}r<+>dfuh$+{-t>a2*x0(}GiWsz@hI#u1ee@i31 z^)J1d-#nXI8dwNyyqP&xYj95{bcx|jiD>r46FUow8WOEn8jghJ9!KrPlRsPxwyoU;k19f!VpHw2le5d4VKEO&Qr|c)dQ|k6*_-~k0*OhLE zU3p;@D_X@y%bpO+r}MMNh9jLDW7~I=aBq^q zY;62HV->qRVZPH<_y9ADnx?Ap+c7i4z0Oll7s{}3UoIxUGxm!BwKk8ti^I-msSJqg z*N!yyOIXxYmba0Y{B*^4FiTS^iI#ZkS1y~ygB}5TPA+#}RVH5uf09S1apKbQrDG#U zb?!Ef!$af>by;Zrr5DMw3ZFeP0M5W;R{0pt1N!F;aOWT2Pq8cU77bQ!!o z;Kai#W~PUCr8W7JX#e?=H7j4J>N|f{5&_U+0WKwDlnqo?3^HRb`*DeRl z8^sea&+~j5qw!sjI=-N8`ecU6Qk)dG=wu48sai2DiwR$vj3am;EQ)9$h*vmrg>MNV}M6pw+) zZ~n;LgP6cQcb@IvtSWYmomI%rJQOZ?U3hpqAg4{Si8T4b*Y8CpZmXT7F)GOM4Z(FR z&aGEnv+Wh*{xKnabZSs{ylrKF$9n%})4Iazc+keJI~+SgH*7-Sgsb0AfE#QEiphy<=2~Lf>}L129){fCjc=%=vb3s5wK^viLp|E z{`JH199?pT;q-ys1ZWxFEDk9CE)?Lo3je0~hdyT?(@@XA2)FWqOl=;tY%EX$SY^Qn z@#~GfgZ<&gEfdvw#*LMC00}0l9P*L}JAm>ez^VEAVRL_wC9rS&QP#VKLcfzO^?nRr z-%pAYv4`A%2>*CrJipwyQLdimJ_~&#u#4H~lJBWhx^jaE=V4!VK)>GBZ~M;g@4M-G zgi2{?!17OupzE-0)&FaM&+8-2Ops*LYL*(ZNKFdrYm7#;M@#U=%a-T&yLpWY2i3iw zZm%n!)=y;G61J#&U}lM7444=&V>G%D|9O8~3w9gWK8NJ~B`M|i$l(9Sj_H3+bld%o z)$%X9{Qk%1PGFUJe^Mj_^6a1DIl4JI@MiEZoxT|>;9t#i04SXY`SbLLvX_Bm7uBB> z*|biCHXfC>qpYLb{fFm)QDW~$pUjw8-$WJu0AIpyPbE89el({ZxA`I8OxmRiETVb* z{#3>YwCcO@M0fs#s^f)@m!H&$R4-?MPOu`+xbED3$l?6auym75ZUL0R572wW?UIkw zTXkh(C6R-$)VkEzD+@q0ugUG?|H+S4xAg|LePs=^w=75wbj&RAo97|EsXrk3^Z)>D z%4|2#Z_^N9i6E~!YZA)#G|g}b@_l3|a;=SrbjrxU9MOh6yh`Q(g1~2By9^cv0fr`I ztQwJl7`J1bfgRa{y$6DvE0Z?P@#t{WcvdUZfVV4d=p*c&fFI99XK4h&q6LY z%0vx~O#6!UaPxZ|BEJF~#23K37y3O@;=gp9V?*1@zNMv3!Mv2ggIv%Z28x-x_yZ}maVomNL~@bjezWi78xITp&mYMH!OizdKx*B8{jw#R-7McpS>IsS6t_$A zw6ndGEhSbgJR$i}&H$_sGGB41yO72!lwd!Zwj6bY620OdXKJ~@m)=;nF3itVpJ51g$BviU-yWP51#-~tv zS1y{xba}PoAK(|0W}(bgz;iX6&++I9w)}g4Lj&-T2Tc|n; zx%N$$bPAp&r}ZEYg3?OFn-`EjMb@TjaP($Pv)bE1g74L2A{B0@4{Y$>U?^fA~e|BtXk3S~g zC8?0x02OBDLtc+=U zG2|EqWXKcKXkiP)16q}N|AVzIn&DbtuIW%XRQa-N=;rN?lO5Mu_|G3O?{s%nxbj3) zt~87IyOa`)W~+vtU06}Rgs0zEpUM0T6kh9qD_rljvOLM}oG{uX+^x(*GzK&IE0PL{ z%(;-`$3Tq~XX-YvPo`taWFzXe=nWVDaZ@jK`$0>U#BXl&Gg4{yrF7h4R%8xfMwXVo zoZkb}OkI=uJ5`Q-G0V2giv@dz8OPcXkp74=-9)EVQ8ALK_{E6iGh7GDjO+%>O31*L zvf=nFZEDy0PK+Z;cqi5PyJsvTkiB_w#gwJNbuJZXXf&)+S2)2{^1`ZZgsieO>-`#1N;Bd=i+VspvtyAA* z3d`$GD{gF3%+jr^%MKR_iClCC5oi*K$un+sZbSLX_(*7ZLhK=ru^#9Wkr5Xb33CLW z=*K_q;9(@%&oRQMr+Mgp%rK9HM3*GmUm9S#dT)fmozH2e%c5`RsZggY8neNS=P@Ux z!FlwW$F9`k24Y+k`LJ6TA=3SzM_QcE<`BG5!aioLuNtl|%7{Ob?G~;dGF4K(Us)A! z1HH5{bd)q!f`%KW4S{tIDr#UN^i69C1aG_=5F}#0u~&qoo9gj)%vnXri*%IV8H}xL zx%0J<^Hj()BiV(Zs3^uA+x6uVJ7qfl;t zuG;>Qxkl}cGj1oVhnj;JfoO`ZYd(Q~es3lWE5FKsX%>tIv|O-nUlPSFk5kCfA9c*e~Z7dcpgJkPUBc}Umb`5F)Pa9 zz^w<|0(hlK^vdA9w^vkYw3Rx(Ol<5n0rAq8$a;IUqX>1 z*mooe^X%d(LxG+LZ+P{!!}r098jec?mmt}l9LXC2xcGzlfl!sXlem&JA3x_g1-Lw3 zimggS2ZH90*|ZsV4u5koF4*P;zfYJmU^g+SHcVG-S7%7?TXsiuGOX*a`Kx_MnG$sj(2VJ>2d~a*7 z)WhigJ#d=gP^KRI6_iD&77b)R^aMNOm`>*S)Uq6dr8z|3;3IOgZJbE8S>+gs2eHb~nwiSiuXS^dDrRlXF zKXp~&d;PZ3g^h++IEAzz@oRpV!{mQJz8x}`+YK1^WEpI z(717uI+$&z2z~=I_0D;xF_Bn3V(1#HSWoswE!ui6kL<&OVeEqSu>0ch=&+M*F?uS5i)5_Wa?&_cn-^yp)4==?{+dPJw-;VlxAo_ROhf6)*4N4~;| zXsoc0OXIa7wew&}gwtwSSMG-xc?x+{j=ywOdUuO5PfcB@mfxG=tov6IAJpH}d#k#@ zF~7N)Syr7iKd;Ipe$CtK$0tvzRSsw-xX`%O$Nu_&v;0}UMBiKK!G`aiH5H}pl{KBJ zOT7UYg)`p2w^uGnf>^YU#W)**)A*hfeh)vOY4#NqF z|LSmLlNSW#9o+rAz_~Fi?f1Z3NdGn3;_K9GOyxVISH5!sU_GBq0ZC_(mlwSS9AdZu zJEG;X;DH=eSbJlinHe`0d8|diU=ISxrwD4gqXU7B2G~frKBl9->s{OjxYe+X0BXW} z^URK{1w3?|4f_cJ#=%-a+(beAyg9^n{&c}SlFGtNk+p{9|22!lzpfHm(0zp9teTZEo00?$17l_D+75he=9 zYBp;tJ3=bP1EXc-L6-O1yx#RSm)YBSQ8sQa1BM0`F7y{Uy2+I2KL zww?oBuzG{};vXwnJ5G!KdvPITf|C$b+DZ&afB9N~bXv$KD;nTW@xV zQe}99#YUGOk!~yd6MQWiaaI~wwi_pScl48Q1!@_wseG(6A}0!LL$ivrb&9cm>JPLQ zxorVTX(thtc@V&iM|H*0L6Y~CaCxbCO~78oiZQPoxn$c?hkL+S9WubxBO-zyF2~H*Laq&>g<}$h{R*70M$Z1g|+C7DlEcNH2Gguhzv+ zJ_s~+?o*?AuyJFkSdUk4I&I|n?D>VaawX+RWgzUROSh&hN($YDYj$KXBzK{txp~WD z20FNzN7+R@&F)LdT8C=AzF@rY;X=%-kT`G3bjP(K-4L<>ZUgmN1gIWDZ%~6<3O=AV zDjS=#Fx|4aPraF>HIdr?rmN#^Uwr#H2=fEwHSi&PyV9~bSwg?@&}bX8>?5M&<3aQ< zC-Xyxj#yv$+XKyQL&C!Rb+Jfhb*GvdU)|u!#fGG=>F~4s>zi+0mwJ_KY{W33)Mt63 z$@GM1-2Pfuie4?s^WT&x61=DOEM?2AUN;S8w0#SCgkh5s)2S0(E!*+ z$)_vutTF!TN)4EH?;=j7dhz_EnC<#6Ojlkq8-0A;t%r+!Q`IB#lj0eWdw6Tuu(p0s zS#TA4EfnZcD|7S4{k*2d{zfZa^k(_(@8@qcG`rS*e4>_O6i9#*>eNN#yDdPTL;Dt& zic&<@r<)DEp*5zW(;Mz<2s$!4ofcIk!nmQ#g%1mBltP#bltj;OIBG~A>16VUF`P-~%vg_ZRV3V5kHzVwQB3@}8tSpLFFu}`&?`7Ovnc>7uzpqEu-D|mZkEy&+qmJF% z#`1RmOHd6<`16c1P>16aN`C7bgBot_J~NnW?2&8Ry;e<`wHJvo3_XCtuF$aQ2=82UzYO=%oQxx{e^!j|prB)my;@ouAV@x^SvX-@ z4sR~QedFvK0nMk-s%M9%PVq-cteA^+47`HU64Z|G`%{co3ZH#}Kiz<(y@P!M(8I&( zn;Wh3b#D8Z$20hzG8gJT`7y2O-9}Cd08>yd5Iv!0j&*L4VU>}ioffd!OPG+2<>plG z#Xor3Ud-KuAhOOG*!Tt5k9La?sNZX{s4%&CU{|>u)SgRb0upe1|4TJR|Dikii--Ij z1*{tKT?;>Q;{pJTYrh1b>uF(FC@4WXn`nHL4JQ}@Kr^Ntv^Pgj4iqjdG5Vtm-CT47 zC@z>uK105DSgea*_5BM@>G-P))%POn~;^06j?F6WqP$kpxY^MCUwK z&R0&|*R3WMc_&@i!Yt-VG~~&4o9=SHQgruV)Q`IfH`A2wB3D z8-nde>N92bdB&5e;b70RCUZ40LftcO8y#!%=;&_un;))h52qgF>;+W1^fgE7Y?Oq$ zE*A95BD{oMj&cx_hKLEDw2_Qz)Ej>WSkAP+0YMz$5A>$ibE$DXuQK`A z!F5I>dP%s$C4MTjhBFZsl|HT*aZIURQ&BOv?z}snuN!d&Ta8}qe#dmG{i4=+>v$c9 zm4;4?nM(Sk^1#krU5zGXkeJw)I&~(4_`fHl{jTfIZ}0sB&@c%)h`MD$I6VV_+cYPM z++o$SI~Oj%D|aq;!bmWmpI%?sm!~53Ul!Q4A5Uy!EAdH9?KZUx5y2f*r^}6-vd+ zb3Esu-OX}il741PiBf*vjn@hkk&S+zjC7vfjam$4f2IHFB8Ain5QxEVMFVw>ZGKW* zP}yqOA{&0E#_SrkaR?HbB(ORK9(dhjPU7uq^*gP^zPN<9;>UeVTtKZ*U2O!xI&!thE7o(Z@uq_@|T#i zP&hU+t8W-yj6e;)E-yB>8#d|@Z*e7Z1ocLseU3^#xsUG9X1)l(s(*Y`+Q zG57KGAv0tJ&aD0Qu9;P|i7Ufpi@3Png{MI)K;#_<^bTSLz`ns|UKj>=z2K`d*FY8n zGGVg)Z~vq~_Wz{#vYV7tqk);J1I>|~WC|;GiQvPuq#zu9Cyb*p01M*wHyY-}Wvy8< zC%4uch28*nE>ufDmQdW$y>){9!+6Qb=R%KUIJyUSk!Xc>5cLL`Zk(W{ZSer5%fXe= zaU9?)3%^`tus!r+64w2t*0|-6iFrn%@m%LW5Xs=L0?yB^gARaGp$)wjzDFiI3Oh1| z6Z?Z=+FA7;d!mTe*f%h06}xD&uIh9u-55iT>hL`Kq?GE+BGbuLU&W*{wgvuahIb!g z-}keN)RKm0O44vz*wfkbv1lV>ymUx@6{bV%bf<(&t1My($+fZsbkyM09Hu=t5W;&kEVu-N|# zKYyZVPpP$>qHV}udMLdTlTxG(n!(LSCFys(5_BHy+mm{V?1%af57+$!JNjAYk+IE} zAbcHUb`ar?GOsx5R*s=1s9jogZ-|5adE3}_kHlL z-}W^j7S{~pX`LN+YYM%QOZzC0me+TJ*1Y%n7G9zIDC`xH8jR;_B}~S#oT$GnpeKeMQ8B zq;3@)x+K>%BMuh2cUJGjLoLi_&YSP zeO?+GHoR24>4}Tz{Ea<)Ok11DJn5}6ukIBr!(sWyh1W!usOjTt5= z5zwH`TgXE*qX2jw{Ak*Qd^P}Oy82*fWi1QDzlr36DbZ)E*kVF}6kUINEG8Ui+FCi( zXn|!HvC?MgTHX*}F=Y8-$acQv1b=WtLwR)F)6=dN-O-Ke`PEI1S&>nz_B7^_oSbiZ zD7ePEA3?r3Vvz*MK^L%+ZvkZ-cn+oyfQ%BydctKqxQmCo(dsiHyAyWKUmce<-#dDv zD+8e>9~tt@#PXUzAQ$O>wfE)mP_}*Fx>{6X60()45Lr_8#LN|!Eiu`5Q`rg0&M+Zl zosfi>3K7PHVPu)fo`~#O$5^vv#yS|&dv@Q?^FGyeKhJ&L&-3}bpZ9$~{o&Io=N#ub z$MHLkPWjHc zCNJ$Pzy{h%Z0@(Q#aEy69oSwYbGPY!& zVR?C+T5#`kArl);to}lRE5}4sGx~vzJdF&kBi)yFuCmF_8Pp7(kUH-2G5ERc`kf=5 zO-B_2w8TPgCU`a3;!Hs^Xvq-+zuJKh>a7eG>L0C&FG+u(TdPAH z30-g%#t4{8*$^ly%RpmjnnsM)KH-6)qAX-tll;xZCrV3+<^gYKc~8cB5i3B=oez*K zA*T_%UG%+@xB&rCLd|QmUQP57ZfVYl)6OTJduxpu%(jVX8OY3C)bpKQI1kTE-qrUE zcOSr4O|O^|r#fF1&Vq(wH`>Z}GY~q@om$DxbYr=`G8d(^ikhRD(v4RW0~QIE%WapK z5JW$$+-LX!x*%1m9zHZXA=StaJyFE494xPhKc2Y(tFcpiow=}i2c~>t0Pqp^A^074 z%>7q4?$JUELD1j(vy;|Cb6<{ z{3g2+VazI(RUl=GmRMQx+}$2y`1u z=sqD4b7srtVtCS^2+#OK*G0J-^SpXSabf4~?$or7x&h*wRE@h<#htwtqA&hK%=X`M zw@0vCpE_UpyBd~Pz@#T!{rI0jJU3r0*LjmWb>D*%UOqrOzWSEWc*3ElDZQ_% zQ*zDDPNi5uwMJk@gLmrm1TLFr$5ed5&vXUu2jP&!o>kZ5Z2l?M5te?eCgI4UN<;@M0OS0_ar&=lP|Ul zMicR9#&Xh`X;QvpM|U%8EUAZe@9ut)Lg?nUJ#biANqk!w@w$Xg=g927G}dp0m!RMUUH3o->3V<=wcRM3*^1o#+fE}{lhfKU-Qkwk+DSdfcH z$0RfGsL={Px8V+%Eb$mxu;b1Z1Pjf>l~Sfj3(lo=?g((?IYH!u1Nn6>x=5c2YI8Vn zQ^*y^6>TLyc5nd@C-~>@027;@nk!rFH=v)$#JKSc*F4>xqp53B^-YO}HsC=JkRU%4 z0;cm5DZ=aM2Q@*InTeJ8iIaN1_;4%B@n**9v;6NJ97g#soO0vE>{`|%`3TSj?o^AW zT2h?{Xy~(2LZ)+06Gk)03yJgrJxb*UJJ=qL5imb6v6B#DF@gMZbn<6EaXxRS!(K9z zVQS(FRHaJpb5U$
e@FxIinsI_Uwj)Nmfyx!o!4rF+`W<3bx4>shJNS7P{PyrQ~ ztnTHDJY(~sH{q9N9@8zGb03NKs1 zxmFMG=)+w?>^u!^H1Nt0Kn#woS~(t1RswuhRADi zH!}9b9EoF@?nMspa5Tr{p84GBG0S8AAi->~7gbaJ#^$2#U-4k`XZ&I<@Axj^?xpeG+qj+h zi-^~ymR6c~xE$gt%oH7NupLfcA^ph{1pj$PWj$Ws8f#m#w50l3d(6?`^f5rIw_IlD zB&`#+`w-zrzRb^h7FdloV>DwI=~8*t`+C-P@ptsn5z;BfOhP=P<)&T&;py9ky0maA z+qTD^Ez)6kS(;DudDF%N3pQoa{sGrRrem6|0G!FQ=+bBLV=1e|e?G!+ncv#NCFZcz2Y{P0>=#XvFMLI7Q?MolG4HF=ZYRs%6@6Lb zzptAl1b03ok57)Qe{Fwh3l^ezbvH?l6rFX5Mh>8!b!7!N5xE|Z+Ld%qDVS`_nv^V+ z>MnA3B=WN@GVP0FyHp(XPeaqPt6iQe_%_XLNxvR1^-CI7T|Gt&R|Z?f&~`fG0;Qz| zi9)lv5!h)BZ>#(wWQVgoTqj_BYQ+sc`lDR@;du;lBtBMy^&a4mdk95N0h)BYHBkm)M5;k7hI-8=^*+A6~bnl z(GC%dxzU!c7aZG4<4%b@ojmbvQTR*8HNCCp?iMFHW0Pm-HjE-@#!hSAstNsg>_;FX zI*M$q1MU0<+U0S5jXgk9qv|w5xd}p$>Irz%Hon14{kFg-r=hmvnTh6FsgK|(u}2jd z95$=)tZ`&)3;;|l+XWS;Qs$5mKo-@ea>bez;Y!Weh@*CfB5!p5hR`eNH%FKTv7uCUCx(7+9>2!f zvCoY+-Y*W7q9M~PhHXL}4e5oj&}@;^2ei%x3%80)x7|EwLwT2pnQb{aMr9H#H*Ex! zAC1vcR1vp$gi%Vx8QD#2mb!HIVf0cu=X?EHyODD+tv1p#oRG5a=44ZuE6gjl{%NyRJX^rc8%SPR<1%%k` zx!$2nb(8D*LzWQqEY0=fj?x=+jDQ>f&GevR7EoT~xabr1fo8d{Mc+#w&RZ$Oi(VH0PZ? zbd~-ZZn8L*kM_(zq?zTUSii_a-jhtLLWepZXG?g-jUS1dDWmI-qlenmD4DadJ(Jv# zvwd!oeA{s5ya=-8F=HK^xVQF`HL*j@!I~CBIKjgya?^2^K!~Ti)H{=HV2}sXd9SSk zEd*PC^xrq|Aq%`)DnZ(i#M2fqbJ0hLHyx6%g}%?h$6mI4@bT$Gj3|$Y&|VT(yT3`p zM#+G(lq;2OvKewb^2^K!T%FQ~yI!%x!8&ogwlh7MOL6`^&x+QoOkP8;xV33tkIssS z<|2EZ_e!g`tFo$hWauHlmK#HHD8m9703f`C0$?s{CxD>4<2Z!q6!BPA2yl-3vQw$p zVrWwhXAY?&auq>%7%gyiMDPT^v!tXfiQUC-^SH~EhnLJA+~MEU{(1X_Yo zJL^qspcT}wOi>V?As2m?QSo}6XSpk_?hW2>m&)YxacyC~Y*C7aEAe9b)4qek{M(hJ zj?DpEC7*z7zrwpoI<4B+loULq@kI^yhVkz6a#S2px{}fvFB==oJOK@tibP`u2TSBv zW+B#%m!nUEUGHemJspqPEU3TZJ&MK_1m>-3P}0+@*4A9TfqcW2AJCouPp8)g|CTFj zL{hGz4>^6LAEiPn((UmZgy$11g_ea4JkmOcC4vNnfCSn|i^sJRxeY3RD08kS@i34E z*BnPj&U{30=X_YK6<1ye%ii1o@2~jdjS=4e5#5sb6Z- zNomj|19*mJOS;=IfWw3$ogKQq@II&UQ~5r(IiBuJdEp|$gng0sJ5cEmIh|Gr4jT6p)0%%i-^alJQZhX0ba@Q!RkpxBqR3_rK1to^~-R2W(0euy0HD*Om- zKpkb(WtVbRenneI-A*mV-ha>Gknq z^`Bq=tx&cpjT#!(n8mDY;(7l?_84e%t-NTL!&xN|l0KZDk?Q<1cVRSywV06?&LDx0 z)d<_}NYXO&Dv4!%9yPkRA6p2=<6>p;W!2cnRIwzpa}8ev!rw6MK;M$s=0n(N7squ$ z=C_6_GM*1wSbw>6#we64RE2VdccU0eUS4#gz{!p=%n#@?Ut`_< ze80Gjee?H=Hk}rRRy7}%#Sxew0Hy-hJ(BY!o2!&K>9KOPaRSB1$`J8Ye|Bre|p>7ZQ@(t+%DbW`7_!;9h6X3)_2qC zW$LxWj_&7#m6p@um56G)2Wp83p(`bo;sF&An6XqcEZ3Fnw6c-J#M|N3k{c)-;1{nfD2rr2MFkHX ztrL#M9UcF&Pu>fy=bCzS=Ea-Kh1)0Ams_6=iN1J~aO%5c_uq&P*r!Mj*d81I>~Zy$ zXjy>e6_ZiN{g9TSo2X%KF^bnx`O4&1Ir&B(u4#Io(F4b|L=jl#mdVwbQU(Jxmc^g0 zR>{ZO-&n=aQ-OWV6n3q&` zbX-Z?r`oglC>;^b&p$s~A-Yt?J67NPJV$JUG#Ap92Z};i86-H@S|`_tT23BHVo_>M ze4$h@W~x3zfXnwSDTyStBQObR-8qfm_P9r4s*xkd=0BFanXNGN#0uHcn-^59X$@$@ zeGaRJN28hP+|*=Elnym7auK2C$j=?^KDkA{b>W)(g?CzCa!l(_TdOP#MsPM)J`CU@G?)+AeDqmTRXlR>*1J*5y+ z;2WgT)jr;&*^|+S&t`jgVr~2*-+wl1I29lm%JU_q_io{1^?mD-P2R-vKU_tCA(D)` z-mqhmbR~fLfkVQm#m+MkUgc)uQ=;UCJqhRPnom5Q zgE(%ghlI8l19MH3K9stZ<+^hlVTBAQmgPmSi)&k`xEjPc^I0^9czHsub8Q*}AtdGS z3Mc|ffn^%jlR)0$oU}ozcmv07%Gs*!wUbW6=5=@G`See?DDt;?^{&gUgBoGsgrl?) zPx2PR6j=6MGY@R}NUHG7J59!rJ29%?*SI_t9ZwY;?b*vpg2tA^axz3V2FdJG64@MOeTt zb*-5Amc&t`GLEK+E;o&WEQ$CYcf^(4IqjQrJ@?IpV}_(}9YIEiPqFP>9N=1OX##~2 zj?g|FKyk;(dMxQ@k4f%^=l1q@TXrZZ)trx?YxaDrIBwde1a3edK^W{32$!7%B)TNl zch3Nl9}yU=;K<&C7|Q#=LbmoC_5}-rRN3d;RfanN)7S!A+4WvI#c@=XQA07M6FD3= z`ucA}&eE^aE>g;V|7OiFy0Gui=#+f>$wi5A6Df)N!R?Vkj>SDo#sndR_D2XGFf`iA z;tCJ2H+tOU;QN4rr|JRTb%F_wRf#=b0-Oq|c#&$5#Wt8MwYW@~9Jlgg8TU9e=p=2$ zT4wb;pjgOi);Zn8Q9R4{OA}&~Dp((=kN_Re<~tuf!FqnctXm2KzLq>V_J>fym5pen z@DYJ4^9SF(VAZ%D=R?cbs>4EJJFO9+a-v3r_ZQ(?N`5MB?bg^_KO$(yTqoZB9G&_Hsw(Cxa+ zwrM&-eD^Vf3&GJR(o5$aZ#xM#cp{zAy<(+T5LlpGv=|f28EW&&(1+))W?}VC68=3Y z`aW95Itf4NTMv{LX#5%2;Xq5)O$7)IEIj7NEgx9OIUhQFh-a2*z>&E@SHyzmY)=Lq zpHuR9;JuF12`aq&?)b!*YBulCLTAa_GjAjv4XpXKs|nuH!ZmKE)fGkdDq?jhV-;CK z6b8DWg+uM+l(=+ri?G^}@%~3fDuqZFQfMuU7i4x8x+Dl#>}?7O5NIx<3-m+D$9wq! zpuj;!(Xw1DT5D!Tix58p+El-ZtdsKrJ%E>@nk6mA9h@-wBL>y~B=Dp#^*b$?l z{;%6}nkURsl`o#E4`(V5$?mhO}hWw6oB(imx18SgG=dPwYoozFY+qkC6Qgh`9n ze3ASy=;i!Jkmm3I??>w6z)dUU1L)Lv)NUvFg8m!~}6+5nv`S*wdBZWWYl( zEwcN*ueqWpQAf>bu)3XtAXYM8pvaAr&W>a0l0snw?g)>mVUa`*fBya*hgB|d&P9ihONm{EZ;!4cc2ql<`h zJJ*o!#TX}%1CPT28YL#!bms=LHTzrr@)DO>pf;eX?u5tSEcq)_TnLvliM2d_?@|a$ zPxshmYndhdq3NPi2cyzU53!A~6?u{5`urQ}0u@j-f(8DzHf1}$QLl#OG69S%bt~~g z!}+xv4GH7n%Nl12Fnlpkm%FL&^<&^rpA?OZmvi`yTd5x|XL;>N013lWHDb_RkMKEcYD8y$S2d!vk?fv9GXadpkpM?5?1m# z-L1gf!eR?0l6qLw*t%Wk#Gbe$SgkEx1%McVB0`u2*eD&bCsC1QGAAL=(OQy*w+@QANy*KJ~RYvgv0 z(OD@`@!UZEr6K<8fTi}nu6Nf;)mXwGcD?;_D=tj>@Uyx(>z5G}g*pK~nqMJ}Tel1H zfoyHLq_NSk!?n@mCmYL1-V+i8;`5*x{)wwn7E(y#>W`%>4QBm8>5seYoAp@e=sUe z7I|PEeH1#5dTdf9d(3Lg*$b9mTN7~BjNF?WaDmzCv_s2|+@>Y~wsJx;`A3aoP>rH= zotMKjH;H3GX;YQPJL3EJU)<3%uG{bHBl7GLR}bPM4Mv8Chj7AK{h>NadXZ(?N`o0` zdBy?!UgPfr@7QV2A_%vJxjX#$Z!okk%|612 zo5nl$5@HNy*4)gu7pvA*InP~954Wg%S!?{I7J9N11>yPZN8%@rKP8@kl276izm!8c zT=>e%A8F}SN%U_Xkke1Hy!DW2ITY^5_<%g2%unSQ2SqAh$y*5Ysl_Cu>{E*KP*UGT zOL&UCOA15!a=vQz7uf{jYk^-C?{YUOv_ zk1b(doD+HwggxxV_=GK8xZFVC??wo|7;@Sc$Z*b{Gl`xa9yF_e-EeGMOq9ta-rnG< zRY^jZwp7xtpL`y-fU{13Jn3&d>fZ0wHpH`NQ9$v=^R)nB^)B-B*($zS!f61n7K=p= zJVkFGDn5eT$@QSLtvK`|W5Gr~L|{pt%AmP2YJj^ws{0tfr;7tB7anBnHU((t3Rjvlo}ex$z!OX?21peIgfdSuYQZQ!wg z22A`%{mH-dw^bO5v?2lk7B@KY00FA7d*K5FihPI80}#XpB*0O^d`2EWYYaIMd=l8j z+66xUi+knY_D$0?T2HfpBkB}|Gw5mqph1x@Air@r;a0+Om4osfk_y13Re&%)0`TV4SV6p~{gKhNSnhLFY!OJAFP~+|Q$EDd z(uZw;^CMT@N5;r_C3jOmCNPT9I70)!p_r38~uKhcI2qD2g@s+k_ zm}!;R?H%aH?Lj14D(yNjHVeGpo6>*l`lq%1|BR+b275;sGJhDf^#a@&}Fgek@(4v!QeQxZ78zk%WsfRf)0A=R`?rPR|A)z2vpN}4`_d(g>vc?Yz09_2M+FgJo&TOejOA}1t$d=CAey^*!HM+TCKmTdL^{v=*9QihbvjRVA-gwXV7H+3yZ_ehi4Ow zfTA_puE8Ei-lQS_VnJqSr>7T~dj#jjE2!KJL^l6YCx<>96!jYFZey*U43_n7wc6;Whx zu0gQ<87lv5vE~_FzE*zm=C{(NPs2c}tlm|5_27f9O7}PM=|VEr*M_H(+dV6{$K}Oy zO1c(t!{R|Yu4a>XeEh9=`B|6zx4g=0oEq^ugdj8E872X0{;^jz#D+4{Un`#KNCRWw z4hM6eL|m8F9zQ&a9><2cpG`8C<+0G_saD7k5qg@uSy+tL1q#Aw+2P}}6R%1yod~<2 zel*+ECI}HGE3%QUoUVermad+lqR};Vi_6&2KwE{0w=fxY5~nNCEuo*^cKIRGcCs}W zs*ZSI4qvh9JH&Z$#PVG!P)t0^_1rr}$%GeGx`tPJ)-xeN;%!I%1}yiA^OQyz3Nxi~WPn9cT*jiy@4sV+Xb z0Jt2M_S^HzuC7@gu7zf#ceWKZ_{xW@O}A3aLKp4@H);FjTg=U4e7smoOWw*TUJpqt z?rb2j0<=uaf$DDD6U=!584g8<+pkU~Mhf}pZy96R{lThKy9-T)&L!3@EZpkCsr%v_ zr_V%aV9!>}(D#1Q8$EwV{vQ?c$?z*MpIa;hN->pPc*mht0P8thO=6`nH!#?DG&eNT zxX?rLTa^+!6|-P}^fI_3Z9*z4elM6{)T1I-Nf6KoZKbjc*y)Q}RcC-?UXl zr4mQQ<2N6Eo)-EZ?9N(EmWE{}%=>tI1lO^q|JiKddJm(R)+umkOQau>6pFLX_>i2W zr@hmw7jJo92T^Y%vT^&S%szv5)qGwd%6@$myytA9ElM93z!PC?-kSvb{eWw~V~?Br z%_LsQNvA1;xN6N)aqL#P|AmD={oGeIg>M&DMcO}I>}hf1(2+!s^Ya02LYw_sH!^by_Ecf(>O|zB)%VS_6o4IkXIzyhTc!pL}lklMd6wkJDOmndIe20xt!#Fgj;( z%_J4X8~s#9-squL!!f0NdcL=dSD|Gpm08uV>$7KSU_^0N!GP(7Veu1tWg?j8(sfj> z_FN}zSmwL?+WIA*3Vt*96l`m~`Je{$+`p9PRv+$D@nl5oi=?(N4Vjh1YZTHw1e6TM zA?=(K&3CgsGQv8m-90y>8C3e`4Y^C(cNwP_MLrOs_WyZm@fxenT$UwHfZLeAlVE`-8Mf23{{tYLd-nF1_6gkQoAZI)haBzqIkfB{ ztL0FYhHn*lGU>Vg_O_|EZT6sZg~`U_{*o3C556Wow~jvjd(r`)MdSHW5gV_8XB4ei zVq7({E=hioTC|G~)r>KJ7(RdSY;9zJKMTJTX6bpt*qHF46Z)b?>L;1@-V=}i$6Ej% zBdVHX>2qw0uPaX!{sh?Q7nD^0#e)veF{<@?1)56H@$uxs!9P9oNUJ}OXlT~;Mp9Co zh-=)h0Sd$;px)MJx;nr-hDh}8-@xQ*26-RVoRLjn>w3z@6PVrxK-BohJ+`@l+#|6R zNj0Xx2LmvN`*PI=D?8k;t|SF+t1}$CT24lIlX+LIg>X9b_uwC!hmys*ke{V)0=SrS z3^^o&(<7Al86L%8v;wh~KA_~R@RqbD1*CS6cHine!d}fH+ki0aH}qGgQ_$iY#M(`% zRZ(QUF`()2(FVBfH$yrA=)7O34xFaeMltr~(fWYgnm*wG5W|IlcXmVou{}+?C}}V9 zBN4Q_n+zbJvgZRxD?pt27C6Lz$2@QW{wsGZ4aVd#lg2(J|V}iB7v%BKMWfn zVDMwt<^MERM!~Sl3=237fE{fyZO^dmknVnEl8&MaTS)EhUxZSGq0Ol8+lT-Bd1QS& zMPZi(AkC8op|k=>UM9fsk)j_)((eU(?f(|9r>QZHVYnG#8(`WoG*AxhyQZ-~O8$qB zL&+L6entchxx9#`8-ooyzA`;dW$cGCeuumXV{DEP+n)n*wMi_H90~%_55cI}-?wcF z2(A3^c}g~d6*%RfLpL{I3^COARs*)G``Zb?^&iayi0i*^z7Rki{n*COM%24XoCZ!m zLV+~=L(nfv{AG!Mv-1B-Ezy;^B}=f|y@ngp#P-Tj0_%LrNawypi}N^>E9Rd}?0SNA z=0L8Ju7Oo6HYV7V#u9m%`OvwbZ?UKB&)KVj;KeT|SB&{4vs~2puLP>U?u}vk)d#=&;Fk^j-3Go6{tedt h;y*Oqe*ONdFMip;FB|w}1HWwGmks>g2KIg({9m?S9EAV? literal 0 HcmV?d00001 diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..fa6c58d --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,45 @@ +version: "3.8" +networks: + k6: + grafana: +services: + influxdb: + image: influxdb:latest + networks: + - k6 + - grafana + ports: + - "8086:8086" + environment: + - INFLUXDB_DB=k6 + grafana: + image: grafana/grafana:latest + networks: + - grafana + ports: + - "3000:3000" + environment: + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_BASIC_ENABLED=false + volumes: + - ./shared/grafana:/etc/grafana/provisioning/ + simulado: + image : ldabiralai/simulado:latest + networks: + - k6 + ports: + - "3001:80" + volumes: + - ./shared/simulado:/app + command: ./bin/simulado -f /app/mocks.json + k6: + image: loadimpact/k6:latest + networks: + - k6 + ports: + - "6565:6565" + volumes: + - ./shared/k6:/scripts + environment: + - K6_OUT=influxdb=http://influxdb:8086/k6 \ No newline at end of file diff --git a/existingApis.yaml b/existingApis.yaml new file mode 100644 index 0000000..c6090ba --- /dev/null +++ b/existingApis.yaml @@ -0,0 +1,65 @@ +openapi: 3.0.0 +info: + title: existingApis + version: '1.0' +servers: + - url: 'http://localhost:3001' +paths: + '/product/{productId}/similarids': + parameters: + - schema: + type: string + name: productId + in: path + required: true + get: + operationId: get-product-similarids + summary: Gets the ids of the similar products + description: Returns the similar products to a given one ordered by similarity + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + description: List of productIds similar to a given one ordered by similarity + minItems: 0 + uniqueItems: true + items: + type: integer + '/product/{productId}': + parameters: + - schema: + type: string + name: productId + in: path + required: true + get: + operationId: get-product-productId + summary: Gets a product detail + description: Returns the product detail for a given productId + responses: + '200': + description: OK + content: + application/json: + schema: + description: 'Product detail' + type: object + properties: + id: + type: string + minLength: 1 + name: + type: string + minLength: 1 + price: + type: number + availability: + type: boolean + required: + - id + - name + - price + - availability \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..dfac1af --- /dev/null +++ b/readme.md @@ -0,0 +1,33 @@ +# Backend dev technical test +We want to offer a new feature to our customers showing similar products to the one they are browsing. To do this we agreed with out front end applications to create a new REST API that will provide them the product detail of the similar products to a given one. [Here](./similarProducts.yaml) is the contract we agreed. + +We already have an endpoint that provides the product Ids similar to a given one. We also have another endpoint that returns the product detail by productId. [Here](./existingApis.yaml) is the documentation of the existing APIs. + +**Create a Spring boot application that exposes the agreed REST API on port 5000.** + +![Diagram](./assets/diagram.jpg "Diagram") + +Note that _Test_ and _Mocks_ components are given, you must only implement _yourApp_. + +## Testing and Self-evaluation +You can run the same test we will put through your application. You just need to have docker installed. + +First of all you may need to enable file sharing for the `shared` folder on your docker dashboard -> settings -> resources -> file sharing. + +Then you can start the mocks and other needed infrastructure with the following command. +``` +docker-compose up -d simulado influxdb grafana +``` +Check that mocks are working with a sample request to [http://localhost:3001/product/1/similarids](http://localhost:3001/product/1/similarids). + +To execute the test run: +``` +docker-compose run --rm k6 run scripts/test.js +``` +Browse [http://localhost:3000/d/Le2Ku9NMk/k6-performance-test](http://localhost:3000/d/Le2Ku9NMk/k6-performance-test) to view the results. + +## Evaluation +The following topics will be considered: +- Code clarity and maintainability +- Performance +- Resilence \ No newline at end of file diff --git a/shared/grafana/dashboards/dashboard.yml b/shared/grafana/dashboards/dashboard.yml new file mode 100644 index 0000000..2cb21b0 --- /dev/null +++ b/shared/grafana/dashboards/dashboard.yml @@ -0,0 +1,12 @@ + +apiVersion: 1 + +providers: +- name: 'myinfluxdb' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /etc/grafana/provisioning/dashboards \ No newline at end of file diff --git a/shared/grafana/dashboards/performance-test-dasboard.json b/shared/grafana/dashboards/performance-test-dasboard.json new file mode 100644 index 0000000..7b227d2 --- /dev/null +++ b/shared/grafana/dashboards/performance-test-dasboard.json @@ -0,0 +1,444 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "groupBy": [ + { + "params": [ + "1s" + ], + "type": "time" + }, + { + "params": [ + "scenario" + ], + "type": "tag" + }, + { + "params": [ + "status" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "http_reqs", + "orderByTime": "ASC", + "policy": "default", + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "count" + } + ] + ], + "tags": [] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:671", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:672", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "myinfluxdb", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 1, + "points": true, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": true, + "targets": [ + { + "groupBy": [ + { + "params": [ + "1s" + ], + "type": "time" + } + ], + "measurement": "http_req_duration", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"value\") FROM \"http_req_duration\" WHERE $timeFilter GROUP BY time($__interval) fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "http-req duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:543", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:544", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": true, + "dashLength": 10, + "dashes": false, + "datasource": "myinfluxdb", + "fieldConfig": { + "defaults": { + "custom": {}, + "links": [] + }, + "overrides": [] + }, + "fill": 3, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 15 + }, + "hiddenSeries": false, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.3.6", + "pointradius": 1, + "points": true, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "groupBy": [ + { + "params": [ + "1s" + ], + "type": "time" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "measurement": "vus", + "orderByTime": "ASC", + "policy": "default", + "query": "SELECT mean(\"value\") FROM \"http_req_duration\" WHERE $timeFilter GROUP BY time($__interval) fill(null)", + "rawQuery": false, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [] + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "vus", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:309", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:310", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": false, + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "k6 performance test", + "uid": "Le2Ku9NMk", + "version": 1 +} \ No newline at end of file diff --git a/shared/grafana/datasources/datasource.yml b/shared/grafana/datasources/datasource.yml new file mode 100644 index 0000000..4199582 --- /dev/null +++ b/shared/grafana/datasources/datasource.yml @@ -0,0 +1,10 @@ +apiVersion: 1 + +datasources: + - name: myinfluxdb + type: influxdb + access: proxy + database: k6 + orgId: 1 + url: http://influxdb:8086 + isDefault: true diff --git a/shared/k6/test.js b/shared/k6/test.js new file mode 100644 index 0000000..f74d90e --- /dev/null +++ b/shared/k6/test.js @@ -0,0 +1,73 @@ +import http from 'k6/http'; +import { sleep } from 'k6'; + +export let options = { + scenarios: { + normal: { + executor: 'constant-vus', + vus: 200, + duration: '10s', + exec: "normal", + gracefulStop: '0s', + }, + notFound: { + executor: 'constant-vus', + vus: 200, + duration: '10s', + exec: "notFound", + gracefulStop: '0s', + startTime: '10s' + }, + error: { + executor: 'constant-vus', + vus: 200, + duration: '10s', + exec: "error", + gracefulStop: '0s', + startTime: '20s' + }, + slow: { + executor: 'constant-vus', + vus: 200, + duration: '10s', + exec: "slow", + gracefulStop: '10s', + startTime: '30s' + }, + verySlow: { + executor: 'constant-vus', + vus: 200, + duration: '10s', + exec: "verySlow", + gracefulStop: '30s', + startTime: '50s' + } + } +}; + +const host = "http://host.docker.internal:5000"; + +export function normal() { + http.get(host + "/product/1/similar"); + sleep(0.5); +} + +export function slow() { + http.get(host + "/product/2/similar"); + sleep(0.5); +} + +export function verySlow() { + http.get(host + "/product/3/similar"); + sleep(0.5); +} + +export function notFound() { + http.get(host + "/product/4/similar"); + sleep(0.5); +} + +export function error() { + http.get(host + "/product/5/similar"); + sleep(0.5); +} \ No newline at end of file diff --git a/shared/simulado/mocks.json b/shared/simulado/mocks.json new file mode 100644 index 0000000..31f63bc --- /dev/null +++ b/shared/simulado/mocks.json @@ -0,0 +1,77 @@ +[ + { + "path": "/product/1/similarids", + "body": "[2,3,4]", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/2/similarids", + "body": "[3,100,1000]", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/3/similarids", + "body": "[100,1000,10000]", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/4/similarids", + "body": "[1,2,5]", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/5/similarids", + "body": "[1,2,6]", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/1", + "body": "{\"id\":\"1\",\"name\":\"Shirt\",\"price\":9.99,\"availability\":true}", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/2", + "body": "{\"id\":\"2\",\"name\":\"Dress\",\"price\":19.99,\"availability\":true}", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/3", + "body": "{\"id\":\"3\",\"name\":\"Blazer\",\"price\":29.99,\"availability\":false}", + "headers": {"Content-Type": "application/json"}, + "delay": 100 + }, + { + "path": "/product/4", + "body": "{\"id\":\"4\",\"name\":\"Boots\",\"price\":39.99,\"availability\":true}", + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/5", + "body": "{\"message\":\"Product not found\"}", + "status": 404, + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/6", + "status": 500, + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/100", + "body": "{\"id\":\"100\",\"name\":\"Trousers\",\"price\":49.99,\"availability\":false}", + "delay": 1000, + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/1000", + "body": "{\"id\":\"1000\",\"name\":\"Coat\",\"price\":89.99,\"availability\":true}", + "delay": 5000, + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/10000", + "body": "{\"id\":\"10000\",\"name\":\"Leather jacket\",\"price\":89.99,\"availability\":true}", + "delay": 50000, + "headers": {"Content-Type": "application/json"} + } +] \ No newline at end of file diff --git a/similarProducts.yaml b/similarProducts.yaml new file mode 100644 index 0000000..7a0d668 --- /dev/null +++ b/similarProducts.yaml @@ -0,0 +1,45 @@ +openapi: 3.0.0 +info: + title: SimilarProducts + version: '1.0' +servers: + - url: 'http://localhost:5000' +paths: + '/product/{productId}/similar': + parameters: + - schema: + type: string + name: productId + in: path + required: true + get: + operationId: get-product-similar + summary: Similar products + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + description: 'List of similar products to a given one ordered by similarity' + minItems: 0 + uniqueItems: true + items: + type: object + required: + - id + - name + - price + - availability + properties: + id: + type: string + minLength: 1 + name: + type: string + minLength: 1 + price: + type: number + availability: + type: boolean \ No newline at end of file