From f1c11281dc67e5c61a5d0c66cfdb6d6aca9c1eba Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Fri, 21 Jul 2017 09:26:37 -0600 Subject: [PATCH 01/25] Initial commit --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..4fa332f5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +.gradle +.idea +*.iml +ci/variables.yml From fabc02de1b958b783bfa2d530b49c1699f0732c9 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Mon, 4 Dec 2017 14:11:53 -0700 Subject: [PATCH 02/25] Simple Sprint Boot app --- .../pal/tracker/PalTrackerApplication.java | 16 ++++++++++++++++ .../pivotal/pal/tracker/WelcomeController.java | 14 ++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java create mode 100644 src/main/java/io/pivotal/pal/tracker/WelcomeController.java diff --git a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java new file mode 100644 index 000000000..264628037 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -0,0 +1,16 @@ +package io.pivotal.pal.tracker; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication + +public class PalTrackerApplication { + + public static void main(String[] args){ + SpringApplication.run(PalTrackerApplication.class, args); + + } + +} diff --git a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java new file mode 100644 index 000000000..31e8d24c8 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java @@ -0,0 +1,14 @@ +package io.pivotal.pal.tracker; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController + +public class WelcomeController { + + @GetMapping("/") + public String sayHello() { + return "hello"; + } +} From 31feca61182c8cf697cb975f2c0d385568fe6426 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 13:56:50 -0600 Subject: [PATCH 03/25] Add tests for deployment lab --- .../pal/tracker/EnvControllerTest.java | 28 +++++++++++++++++++ .../pal/tracker/WelcomeControllerTest.java | 16 +++++++++++ .../pal/trackerapi/WelcomeApiTest.java | 26 +++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java diff --git a/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java b/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java new file mode 100644 index 000000000..fda0f0f34 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java @@ -0,0 +1,28 @@ +package test.pivotal.pal.tracker; + +import org.junit.Test; + +import java.util.Map; +import io.pivotal.pal.tracker.EnvController; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EnvControllerTest { + @Test + public void getEnv() throws Exception { + EnvController controller = new EnvController( + "8675", + "12G", + "34", + "123.sesame.street" + ); + + Map env = controller.getEnv(); + + assertThat(env.get("PORT")).isEqualTo("8675"); + assertThat(env.get("MEMORY_LIMIT")).isEqualTo("12G"); + assertThat(env.get("CF_INSTANCE_INDEX")).isEqualTo("34"); + assertThat(env.get("CF_INSTANCE_ADDR")).isEqualTo("123.sesame.street"); + } + +} diff --git a/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java b/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java new file mode 100644 index 000000000..bfa8271a0 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java @@ -0,0 +1,16 @@ +package test.pivotal.pal.tracker; + +import io.pivotal.pal.tracker.WelcomeController; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class WelcomeControllerTest { + + @Test + public void itSaysHello() throws Exception { + WelcomeController controller = new WelcomeController("A welcome message"); + + assertThat(controller.sayHello()).isEqualTo("A welcome message"); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java new file mode 100644 index 000000000..cc7091ed4 --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java @@ -0,0 +1,26 @@ +package test.pivotal.pal.trackerapi; + +import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class WelcomeApiTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void exampleTest() { + String body = this.restTemplate.getForObject("/", String.class); + assertThat(body).isEqualTo("Hello from test"); + } +} From f0d7722c5b8926809ef41f506032f79cfd0eeadb Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 09:23:33 -0700 Subject: [PATCH 04/25] Completed exercise --- build.gradle | 29 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54227 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ manifest.yml | 7 + settings.gradle | 1 + .../io/pivotal/pal/tracker/EnvController.java | 44 +++++ .../pal/tracker/WelcomeController.java | 18 +- 9 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 manifest.yml create mode 100644 settings.gradle create mode 100644 src/main/java/io/pivotal/pal/tracker/EnvController.java diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..bc99220fb --- /dev/null +++ b/build.gradle @@ -0,0 +1,29 @@ +plugins { + id "java" + id "org.springframework.boot" version "1.5.4.RELEASE" +} + +repositories { + mavenCentral() +} + +dependencies { + compile("org.springframework.boot:spring-boot-starter-web") + testCompile("org.springframework.boot:spring-boot-starter-test") + +// compile("com.fasterxml.jackson.core:jackson-databind") +// compile("org.hibernate:hibernate-validator") +// compile("org.springframework:spring-web") +// compile("org.springframework:spring-webmvc") +// compile("org.springframework.boot:spring-boot-starter") +// compile("org.springframework.boot:spring-boot-starter-tomcat") + +} + +bootRun.environment([ + "WELCOME_MESSAGE": "World", +]) + +test.environment([ + "WELCOME_MESSAGE": "Hello from test", +]) \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..51288f9c2f05faf8d42e1a751a387ca7923882c3 GIT binary patch literal 54227 zcmaI7W0WS{vNc-lF59+k+qP}nt}ffQZQE5{wp~xzc30g#d_WEY_lIM<*>kEH?T**n)L|d4_rKbms*8e?2n)Ki&=O?<4J<%>V7R{?iBg zUq0qeh9)+q|G%!t|L*GUWa!{v>h%8_vhfuLJO%*-l>FD9;-818|MyT)4^v}T7kei< zV;e(f=M?n_Z{$_P@0tV}PfeKL81;-1K%hd2jPC7;DA>wd?+UcoR*R#W($?M))u>gd zdn}ehUd3m+8_k40@?K-j2J`KZyalp1AAH~OemS09)oMlsj}Ip=JKY&CS%B#&?VkHQ zbp0RoxZiO54G4JffN7@lTM-EP35bJi7)bfM8EqjCYYcnn{VS&2QEn5KJxt6fN4jA5 z35`f+Sduq7yq)PG@7FzVPY-Jv0EQW7A|u)@XZ43(l=9JiPNOEX-?M91MKSb8LB7*ix_1 zP;pX`ce%>4<(cfvymJ}t(%d{Q0z=8R1bQv29j?-2sm$ONs(@PIGdy@9R%Ta=*YdEA z&d3ZtFAJZsYhJ)vf*)knsFyO}Y=592S4#xG?1vm(x|kp{tuD2chGmvsN`Pzy^ zUV@_Yn2NIb#=1!#ZV!rb;}EdSGKZGG!tBKgC=%PM#u0uc?HGxKOZ9pZ7_T|A*1#Qy zf$Rm!>2sH}rRl&DRAmM3N>s80GU*RYMpwv5BW)XS&*^g<%=05?3M^7)<&I1!aW`0s z&|9LT!iow_V9V%qPOf<7C7X$)ABMd05mgn;wfKiF_Pq*?msg;KGz$4;ER)t4nxfpQ z>NJFiAO81*#gMz>5y%b5ZK(1yt)P-Kre5KQC3YH?naRFiq=b zJGw+CtdguK-J8U(x~Pj8^~>uka;Ov~%`0$5NxZck$uYX(d75Q@4yqjY9dl38H@E9W2#wa*RJ8+Fq#c%!`8 zF?p_di`A_*fY_}yp!i7Tkv@$(lKu;SFRYmSf^wOzrJS-o78?qR;M;FFvyw!qlC@@U zjOGrCIjd*tv02Jqu9Kx`kDN_RazI{F##4p9_>71}duTTOg+3*QxxW8&f8T&T>SwHa-%rxr1sm0msfK|vl&OQI zX&*rkG-JX~)q&mH7lf~Zy}Z}7U|~=(6%wDv zXggVP{CTshNqV&AXub;T_43>o*E6$B1-0epUkaVp40X1y$=!96kgUo zQAWHAQ4ln6PPLTKRtm|vB90=hFW>%6&a3Vxd&+4)-RLEH+7&*$N#o@kG-=jl9>#1{ zmA_w6RaTULUkhTUPuuirdzrP-E*H7VLpxQbZ1nM0jU1g9WR`IdH%?agZbu88eAX!D zmmH#^1wp!tnHJ!>H+UD~o#?l=TaYt97?r-!bgjq6OdAYtzBP>2kC7H6i5oHEphkp zXwA+ySm+72eNV~9feSko<*D;cffr6e&cy%;qNZSI2spGwz0q^o0ocX!Xj{Q3F_FFy zb;xtD7*4?WLK3%$cb>s{W9Q&3(PD@F(i%LS6uG@nCDEj-8k$@3;>2k48067l8*}^f zDK1+;{IRWcX9VNm7Upz%V#NEjq}`_K^*p)Vza)4Z!DS(t4nSte6V)4t{4zL9S!Rt( zG{n^ISmKsVo2QDFceIUfS|WN{o_BbkcY09LPkC0>vo5ZtVTJ0qcQ!3~XP=nGv1 zJo-kS`382}lM(yeA}-?&GjM11ZN}hk3NG%6OLm!3viy4Se)z*zljGWqNMTRT z;|}+51hU6pvm>GAzY!k9b#*Qp!4uUd{BHHM=>z7(TUQ&61wgNHQl)lHtm$Tv{V8?< zk}YmbB)h;VK~-h)&*iAh&3CrB-vGbFBvrq;kS955Nc(!x4d&^0{<$rOYwALrDOsQ4 zW9hcssstO*k44cLQ%oj=Xj9q|<70?iI9T0cS6YjjP+BYXQepHl8|&Ti8B#65lL2A6H|t*f&z#g5Xapa^l!(# z{3O494S#6RU}ob3Jn||E zC_hOlR0p5`1g7@8F13LjC$1vO%+&M*gnInCca^I=)t1-j*Sz;lm3mi<95Fr|0BFc+ zu7#3Ed?%B~Ue(h6*BMLO%@xB;6k8MgnjgMWjopLNBA9B0ynXzYzi2B4s?*J`Z6}M9 z`z+Ety}W&Vn)+KW)ql|+_RkE4d_;T&aHIwh$qd6>IR;n7%`5{W&?Roh16J=vX;Sz(L_>M=+Ys zq^-2;)!L)YO}{_rzt7a@)sp)32M`bz91sxMKhIRb(8WT~+1b?A$j0=4K%d&1KK5VG zM<=1BS#3*W%xxfHDuYZ2T)91JD|?Vi-HHU3Og$negw~995uVc6)mWhJ)ggOB&AZU~ z;+wRRur(+D;Qd|Ry1T9`ogYs2CU`ULoyq=?b2F9I_Wd<`@eSet_Z_{@6D^!vJEqVt z7M(~r1dExgu9$o!tEoXH#W-y6TTZhWy|CWY%}8N?!c8UQ{2+>vnqAL)pG@)nixQ8l3#ZDiH- zOAKW<`aqk}2YVOsgkJn^o1&MTTwm?Z7nNVZ!KSmh7M)yntYx|-(mIs}Tx+Ef#=Eeb zJ9eLf!`0Kz#>vad)zZ+)?`guu&dSx$k&oM1Q_$x<)K7U9#kDG4GatB$p>pq5WZX$- z+bQkIXhgxm#Ln+Iq9S7Cy@D8Adlo&0bgaHEnN4(v-8hvgE=x3sj3_QM^-ND`E*D2f z4Iz`_sKU){3q9S;{>cO(!Duz;Y3V>sB$$VB=phfv*F;pxRVz!1i{0*%QQ}ZK7ARHbULmBp8A7 zz93B7-OS_TvFL!)V<3|WKIDk{_$C$Gpw}Sl&YiUR!TvO4y;-YbzS-gk4rR>KFm}GQ z(y0jd8o`kZO}4_uCe0pGLAzRy6L%bc)KC-;ODa9{Yw9EW>F00Sp=p|Fb9e~NQ=P`B zC!*duS%7J&u3)s-SO#I-pRb^G3l)C}q=_KtlE@yr-(=Lj`miy-Az_O=REB;!DvnTN zk=+WzuHH6>mN8TjMp^0|_N20YLb$W={LE4B#2oP$p*e=t9*Pq^A%CUB9JhGQs200( zhV4T*OKUIM`{b^3Xt<+B?OS08(%d=UIwYp@E81^+&57K#eg*w1-RZiVpAJkC=VrCOt1UIT|y#IB$V5l}&G8kO{eaZFLGx3RoJ&N;4O9yunGUqu?ea z_(0(#OeEfb*Vg)R}!1*oms#bJAGG83BAD4+Q+e;y0;RhEa~F zz`Oo_z!J)KI)+4vzOeSt1mYkFe~D1AC&ZuJlB8ZY14)uVQUWpt%(YK?-imDdvIW)V zRfLI>P%DVp;?PA_5xP*fg4*Bu^H^|KM6F7~Kh?ER!vNwOE@2!hj?JMnQTrdAW6x)! zFK1p03IGKY^f7g%?fq*BSY7TX2QP&EbQ6jo4k+)%X3L5vc zBgoN{WOq|@unntspvNN5q`cUiO$=zt{SNkTy)J;pf8r@$(il*!`~7x?@Y+~G>*n=) zOt^zka-hG2j|*o5^BkI2ePjqWjvNycLpt5jBDy$f4bZ&vTS|W08a~$$$HtK(P_@p8 zMUUu?UUT1Fa_^fo7g>yK@y-M_sE#1BGpaSmh3BY-tK<}L=50V1>P|u{*fT7yL9RD% za;^U1y<;Kt&T{dD>E)ZssbT7R>Ro$ff_p}?@(ZTg`{ZFNUj&X*n&bB+ub zq)0d%*cs3S34)l8068Ko@q8d;1PK@jgqshQOc2Q6-z+H zuPims#_D_e+utKms(L7Wt2x0o!w2`NyC1Z>_E%p=yZCiLYmpC*HUjFTD@EG0@>Xl; z;$Xa3UV!idY2D{4WqokC@;~{7k5xzUT5wfI;h|+z4l1~DBMmU}V}?co!o46N9BNe0 zDe;atV+Pq`p$GL2g&;#xdAflKR2&Wfj1gwlTZN@PYYIya%6gjBly&E7;;c>AYIA>a z@#lO2-Roe_1)23y=JfMB-bqXw)bg2jxp{WnzS(AJrTm39v109e?kErRK^uE&eT3(g3KwitamUX%r=36jqIb}JtYaiXHS`+tt!u3-IgyIxh2=i!{PiU ziqcdkUO^Rtvovc=tj8HeMGMkbTq$kRq8G)N^)~Cdvy(;*i!M`wXNVS4Fc2VD<8$zn zvXs3mw`%va+$rukKI0F?fHJ01Tk5D9*{1GmEDpj4z7$KB^@mO0_x7xG8+Lx0IRqyz zm27+_T=9*OevoYpwS>f`eMTl{LyFZYqUGv8_yQoP6ME$)Z;yA3lf*2m*yiUvi(#uf z1;-wx@s7ARy%wQ~djP3Us92T<6aCTY08l35F3~rW@xs znO_V+$s1G;i=QBEw1vU3Pwk}i{3#nP>~+JmQW|4q(Pt8clH$ynV2|s1=N#aLJ-&Wz zS2UV#f?iy?BhFm8W6(4iv~GSHV^NXyko0FBm}Rcrg{kU}nVx+G#`@!mHJpHyeqPKr z6Lgrdr^rU7D#=j(%P zZ|R{u#_gd#2JjFX^5)tZ`w61jvtP`x1qgLI5q4`4#CapsWEhp1ESm%l$mnGpL}T+| z?@wZ5@hJuvXD{AH#L6?4?la@&8AnTMk;H+}wz06RNj^X;ZwuB7si%=< z9lJM)rS=>{;H#5lp=D(=n(k5}s5D6G>Bm1% z&25b2t5a2TTv;n8-Fpr!v`+I`w|y7B3>^u0nue5~^_Wj1wrQ)-Q{lX7w%LTLapV}S zU}NSrz$z1N7!KELA^`=x@CI}k?Cp3e+bR>gqs{FQrlJMm(rzflHaaJErBK6;Un&*o zH63;rpuq`y`-PhKB30SJ#oBo+q?##C--15`GXw<&iTO3N-?|szKyHO}5!NTPF%2u0 zmHwP{wF&^k`4!WVVGV8n=aBM5Z_@{{(DMnN!rJZ?_-Ge=P#dHHVpXj6BHxeS%7RQZ z5;1Zg@2h}-gOZtugHqU(XE*BOw#HJSWIx*1at zRrG=z+kI=U#BiDXcaYY~o@1WMB|UH8m?VuAep2nWY@x&2tkj;XN(pgME;mT(R%;^N zjPg%R-0mgo>D{8sXwPeCo4nzPuhh=>sCHglSrKvvajw*GSpq}{{cl#mebopG^vLAA zj^Jf|{X7*fEi-Jpe9;mo5bN{nL(fGVge-dF8Ue0cf&mB=B<1|1TRRMi;XYY)+8Je6 z)|y$Pr8=f}|Gi8F#A>t_tDn>=HZa$+R_58Ntusu@2Is4c*i`s&Ov~aKz)5xBW)nto zHe0i2-#|aF*kXsT+5^IFNf%zBYF>ZXzUC^W&C5k~Im9Hr%A4Gwy8-Mk2l#(8j_49a z+7)`}R12t))N8XC#916}iuKwGx+@ThYtWOO?D zFTB$Pyvq-=m#?4yUaVOWSj9I3{}m=cfq*#vIRX7!u|^_eZ)|8IV(IMSWNGB;Vrg&p zFV0>`TN+ys6LOpaue7UiNVcXV$m-QIYJ`mi2d> zN*=@RX=EOenr3$$(wpB_CAIABy)kR~B?22(#`W;uA0{H&-*Wy&8rL83?QX|_zrLmQShDC*nAJ$v8q7U*1(xBK1(s;q##37*2IvNSh zwr=LR#R|?jr6hQdK(?fo^jC?eiF;+s1TB)E_FBT*H)&U{lIz3qbtKQ2HY+B%ep7+n z_ytKe!a7?!kSGrXf&p(ojv44{`cs3Thr55=<*1gV$VLP|dskx$GL&l5#61gJ^+VqN z9lx@G1&;#?massD3C#waJrev|_Q@qTPP-4ixHhrEBevY5SFq5d8Q4qo1sobpScc-% zlIapW4WVEJwW*mg4cc2!OmwO-agkj!o}pVvG$tjGyP_80MsjiUD8o|l1)goh9L zS`Q>&2k7Nq1h4u!?d#QW4*DljC&<4?!i!gRKm1=YOW?0q#PZLP@IU+niGq{8ho_*c zi-oD3i>0yQ|0*}iS9VB(D7>J_Bc_&)s`V>Uw$>q)G_RKpcNod+9FgK(P z%wk(;FCf9Dit(O)wTUp7%@pjrN-(0z@I2k{h& z4~F7+(KryqTkF$=|I@hh${li3@!mq=>Z}!_Wp_`D>c@#H29zqaM;mpxa;5b0y;dYU z=>8MxjZ5&o-fuq)iJdJT?fk*|EhMk7g%2fGat8fS80}&QYkaDB+Yeim^_98)?{BZ&SKW{kHEC(riD; zhlrcGqj$)(j!z6}4Epde;Noe%4KH~04;znvE3$&p$l+!b_DaXNtv|Yg>(s(wBAM=? zkd)yySesZSr_g#ctWx5@Hu7K!dX1|%JHv>WYg{?5tVAJ4Z16Gc8;#GIH|jI1bvK4FrXAHDcrx5L zkM{>n8}B?MRqQ8SYnMVh8@B)V05kEL+D-f$T)cmkQigvHFlA3?7gO86(e+nObg?vb zmN2yYt402cA&ODcR@PQU`C^mgAjFyIVUf@-ZVA>_qL(0Tra~b>1tEnf(K{#1CJPI7 zJ{Xq-wtsrp(#J7B53+}w;h0%UWB48I`yyVkD@Sy=GGWsAykYI@ebv&}vMTrW{t?Fy z+#PQ3dpc75ixU5w;cUoNhC2OjfH=nfE#E**j!3DiV9yrZ8S2>AVz3xpk&{lMfnr1! zUxh}}qZ)D2qYRN+Bjq4<>#!8vnr~~gOR1ZD#*E=)8=%sI;kT;5+0HOSUygZGV4cfcL~k zev9V}!;^)ZwQgHl%TqF4kftMTOW}HGHhPR5xupqM=m`xsqSV@km=wJ2rWRMtX2F`> z=zJTRZ4~m@9CK5qWZN{#26CtJ87c=pMX|L;0VZ)t*iWd%YU5&|2d&JD?$B}L(~Z=C zJ{WAqMa{B3A{mB;8Df7ELof2$GTb4iGw1C^cT9;UxoTBTfXkcvO zAL3X$>sZnx{uI6mO*>g(&kc|m(P6r#R2enAp)@+qr!%A%Mr^|~3V=8JAN-S|BVh8% zC>`65(EwORVnZvr6|?9wb7$X!^xpK87Jye^cy5^r4@FvKdpJ6nR@_U*GLz(W2|=w9 zsLrlp6l7p+nTnQJcNI}!F_iGAU7ey+;5A@4t+CYD=8SZvc0yU!EM_Ux4q7L@)pqiF zTkhP`^n$lm`$qTJNp*zmS~5YY0bH5Pq(TBf4Bi?Eg9EOK`chD}HVQ{Cb{w9c z|4SDYAApRScfgFA2MCO$zb*Myr{WW15B5WTM+t;BB~3$swj{>Z4yBHI^ONmD!4ufz zV$vBZo6{}jIYq?dfRdUxXxv4{NE~GO#VCJ5g@jcnkiK=Z=~i!yD&2z{WLbddhqZ}< zk*3_nO6z5o&)3uCSNipDK-!K+=HZZrS$v&r!`XV2>22aze$56|K1qv=%B~?qTL%7w zGQXFp78SBN|D9=T&Q#?-$y?|$QCdrt{F#{$h{goO2Un8rEXu0;4fqhaWg*@tFeBqP+0OptF|;X6xeNL61nagmA#cXZQGqZ#*UV zO?`Jx#RHURffxb=V_`7{85DJBdV{hg_S%G(YOaP@+0%nQ#63ds@CSv3|9lcAvx+^7 zg^4>^?5(!_blsvw?fV$V^C}8cScRJxd8}wX#?itkWDRRg@NMSDoGAbR<$%m5fM%n4 z1Yf9hf{kwbxx<-nd~&+*58loncHnr8^7_HI5?q%B@nRd%Wx$F=`Eq+GY z(j4pfcwnwGU`-Q(zw5lpAkCmiBRAmww;b{T)-QV&)AN4N|M&GRrwZeB>2EF12LJ!f z@Y^}N7~0s7F#b1a%K6J2pr~W|$v-nqngt+eIcQtVi>=YXLKi4N20}sF3a1HR3xOHC zHgeniAY;C`q)DrM3TQRYuuSg_CvAw9-c#rxH=zCXxEx%!J%G$O`g*f(n z&gR_pNqh0x<=*A={MfDQ{b7Lp)e571+YW=Dyf4gz!6Y*tFHaQp>j!fLn4A>_S%Qcl z+Ty(lse{z5=Ynrk!K+?{(!jXzHy^Z zBQpLNIoF+}k5s5Uxr5+vp5#H(s}53LnnBX55QseKZ8Hxa@eumRm$anNS7WF-D^J-z zHI%M7OxKv(L#Xc{6|cp5>0sm*B_KMXWvjZ{o!TW*Zh-1zD7CDEc^J=m@4 zUC-W2IZE*bPdOZ}1KiBlbjE3Feqt*5441XE7;c-Ba*}bV$wU`f%rp&3VNM(;4AG`Y zhmEqsI%4|K2GCpNGAiU+;;ns=^=K~jupEa{{2Ny-ZqT-XsL9@3+$X8af%k9EME7DT zdL_NPOp^kh+bmbBH44fSBvY|XHcOZ`X5oh&rFkx+ZDY7cS7!D}IW+|D>J#b;&DP7C zlJ5<+mdp=yJCiyCZU;L1R1!1IA@z=sce|i!{9G>6W7362#;Q{czmcL<-ze0~gZP2B z$dioNJY1tMFo~{uW4n>bEJLYBt(CTLENJik@lCsoBR-S&MLO+~RTrMPx(kecyPQ8I z`eSaG*F$i)+>U**F9vC`f<8gLnZjqUWOynXn^Hx8P$%iqb7P}C1jpKLZPgs3ytT&M zO?bo6U477Z7w>#A{RkS<{!--p3wKO(l*6sbFte+7#-4wWKa&+No{Aq}5AMvd{AvN{ zuO+ei`;DRME}vL^^N!$6T$SyWhH_)9-Ie>DvAdCZyMar3yfz!9^l}nZWGhcIi`&*7 zxe{!J*)rP@)j;g)1*DYv=RkiJ_+jaECX~u^OjvXHv)OEUGTI z5%xHYFBW9u!w0hrT@UIHhwIc2>nvLqCs%W9^O)Wu$yv-78n(g5!RM__FQyUbL8+!d zx2$|y{ZX!sR&w3RPG%bvahWHlMCV}2xW~RAO*d)=PH|awjnKinvJZD_E)?te6>Bs`87e+# zcT=hsbqYzg=jtQJR#{G>h${J2Oh^zT$LJbxZ?oz zdCb~Js#N#7J%y2ps_s7uSQSRgqSd=PqA#M=1p*NaJ`G-QZMtx@UHSoNJ;4?x@T>Ej zTYR0@L~`_)=F>jeffiI+BO8!+d|~cODqYGdr&R}VRH}~VuTDAb@}hNyzF-v=r&qNR zT-BUgya?S#Z#)RnBenZEk?66!_m6ZT?C}`kZD1W?f=Z6pq$0}D$&s@K<_8P@dUc+$ zs(05^Rf8U%cSogY?3S5f7Q>3}_U+Nl?=_qRb27Nh?(GSlIN{)q#?EU~I=kEnk-Bhe zgv-7-)|}78vT|2Mc}^w^B%Ue+VWs1RCx>`qaJq&myTv2B4fx;v2wx$FEqFhd$n>O` zJTzeU^DFldU4g@v9xznt59+-j<9<|U3^ToZ0>LxNC8uWHG2|V*l54X33NnH7R0gPM z@kWThsu?Qbl4J@WstaC?QDop59=188%fvE`QhD8!T^Y0Lh2D%}>{E~`N{x0sClrhc zh5pb_sAo^DD7?8xdW>DppX%D@Ks@b(9}9W|+rjV78vdCMb?dJ>GRyM%LI3YL@y1Il z?dC7$U;c~vY5y7X3)$Pd{ACji9b^qH?UY>%om~DG@N2p+sv&%#Or{cxpqT^zlz<@| zo-tCEvXr!dYKH{N7BGfjEkGIRbpvZmo?s%`M(=!qzuiddO-0X2RrY${@So=w;#O}& zvUkYLc1~OG%it#d{`2`_4x~3A&m`obJ!&{5tr71aI7&b_p)pK~nU-)cnFxcQYN|7! z+S`ph3{_Sf)K0eF8BB?}t&!p&JlgeJqP^qiAB`jj>hPELj!hrWJbL$E3Z~V>)P(3@ zfU)jqVG4;g1O$#E%uHVLe)O0cD1;=lSjba3%OBcw=&Kp7vbw7gNKzS^s`{$5F}v0x z^E-1=AV!pnR3uQTnTwhSfhBf!Ow^7$ELVxLX{4&m3$0e`Q<Vw37*ldO`jX2H?M?}WZW16~7e3r1*O?D#={<(WU@*&&8Z>oH)j1BH zB%EvM?N_q&o`0szW@V-vCm*ymTP>|i6%+FnDep5Aq$^B`y9Q`}PB}DF%1{3u={{Bw zZ>cUaF{lZtV-CU?EDV5)fpglR`8#UQB4iVlZibeE-TP!7L=jt31C8-Q?RZdtAYtEv2e z_^XD%EZy70yKZH0usn)SK-#O+fUu8j zz;=6SPkSiD{5aBWG&)G%j z=GWkI>em+7sEVaH?T2XSBrpTt>Q64PQtbkt6xSiWGFLpa(miCGSqZ*P*_tclLfca9+#UIs42wtVx zv*e~MD@-;dAbr*MklPxeIg~q#GQ>HBR?5QhBY|#|y^;bCrJ_Qz0-J|TuyeSxwlQ@J z=5GwXd2e~}AbFn6cqQ7u+ydRcMNkKgxm*mQR|15$;*6WAYJo-(OLFj-$Rr}E`N=sW zoa_b0>s1*Tq0sc+R0B=`eW1nVPbFz+A_>oo>)>oJt5*0=%iAHupM+c|en z1?0MdH!=h`2;oQ*uViOC?!+`-Az$J;H|#JF9+zOVMckosT4BFGfI*xPk{*xvBMf!= zA=D5DY(XZ&hl=`6#WbRF^T-&F2quiN9EFhjaixT_ZQ_U`0FT;YB-Z4o->O2>wz#%lGz(!3Mwd@goQ6zrP+(!k7vS>EY!Wltd3_a|U4&wQ~a?fHTP?#?E{q z2^`Cv57~a@Qaftgta9lPRbFza&z3gO8zG?Yw^p( ztmyBGx%}6N|9R#7KXlx`$_d|S*}n}#2qC)`tXkA=^uM52Lvsi49SG@o1Z{+EASER$ zd$OS6_X>vpa$-Ekym(VSbuR!&0^z}a3?m_*R`#?>-wIO0&X$^aHIxgfEpWqD=8o20 zmUD`%Dq0M(;1JhHYH}!A&kGS<=`nx*s%Rh~CkC<37_=Q}c?oW;)jg8~A2rEYwbGUaC^6rW{vlW+!}y<| zpGDCpt*}TMP4T(8&NrNS4^;kNU+=Jc2=Yl1duAYM4%C60dJ=r+;=K`ljPXCT1zv>Dvcqy zIifqtMuQ8MT5xseaa|3Zj*5z6#kLeXTWg)mMi5wSa|#r$ax3svMpS5Z&pKia&u9}A zu`j1 z5=G(SLOf^(kVlX?OhcpJpZ4$OIz=FDGt?1QwUG_g^sml6XO2xtcr2#D~XL-K!qyMNX28jwCH ztG-{k&0Wk`KM2tfXx8f2Mj>#i0c$u1^$GC?4Rp;CCIf5HYQ&azR_46S(7){#I%^kk zY`4iQvAb*+*er2RENoZx5)j7hF5*9W>)2!ujxC%Y`4-6Db~cys7DA8J4R*T${yjJT zQ~s}Wal3!?fVfAJ137Z71~orJaB@?Jn16)*iUkKvyQPD@Vao?J2EOcRg5HccJ9+3W ze3syRO$d;GX$?VR?U);|)?!!;e8B#t+>^)f6NkcFeTMtR?-)6Kw))9$B|-BB%Z_d{ z$gJL~V`nj6d3y8rqqura2qNi!7JR{oyZVgutKYHz_(IcPOh}CCOA_j(bMLL)$6g4w zpV$}W{OrN$B@MHm+~;c5+D0Me{Bd=w|I>pBnYO%qEKbABFc}*rxWU&V=1^O z9Pf8DJT(fTlbJxJwv4b?8KKf`J~e8g8|>65ifZZ9AO~-@GU_2?vmhb@0%p#g-mudj zz316PhKTE&8F>@-^k12IKTAI1t`&GVUHhHT@Fj9^-CG10a-Yj=@1Ni!zj#wy$*X7b z>{1k>s4sN)G06A7ycYRX;cshgg9v7tnTnRB%=&@rUg|yCQp)7^0=_zNWYgxYfP0NR z*N)eEY?I00F~eo=YF>RQ<38xVxQ>a3&68WNOMhiT>EzFq^$ZX7*s3df?8G?f?j{2P z_MS~ysmq!^voZA6A-TzGC~KBXC^s=7qhY62E1d=G&nBB)TT}L0?@XBGSWUU%EjuwT zS7v@++k>Lu8Z^pQp_xa;2w`JMjg{8&=lZU}V|=N3kvD#3Z9--`Gtg~)%(#@HczTu)Tz0vtTeu_4V3!u5{)}$6XdbmA>R1suiJ>%W#Z81vcnO-k{Tvk>> zjxG1HkZa>-H~CZQc}P3;e6-$WcQ#w9!m_>gcRR>AxZ7m(6?(=N;bxv|_{k_0*J|#J zg0}lz^kZpFIJ#xa!9tB}R>HWmNg`jecUx@xpVTZ#=}K2sm7{tMk}X|Pj6_Bv@{1}C zl?)7(pNE>$ml8+JsrGSchV8Yz>{d6`29uR0tIo+D1CE|44lx|49w646=TZ!ZJHa_Q ziAR>Aj*3)pRn+1szxF*wHWm&&mb1dPmS{pqv%9kzOhWbcq1qsZ)7N9Y(IO*J+;U>vu59(lgYef z8ssn0HI!$T6P#=yMbPN+d;U@%RHNxE5jI4n5ul|U6n2ZlSEU>zUb6G#&shic!|Tdj&~hqoIHy@j?I|fZXeG zIAXrM5^R1$3Bh${({5>EL26d6!9$#S9q%dU)adGO`>mHq2M1TmGe%01rzCPzteMRU z@c3~_2I;(yrXkVjUg9|pyRUx5ZM8nuM$emzn5k~LMXreH@G$mmIHlMTZCn(iGRZI` z8r(Q`(S}U5dJ3Lwbe54&cCk1$Ysj*vlBz4vNo#q--Tvx|7OtE3WV8!v zeY+KFN(bhth|xCss>}_J5s{f@o3Raz-qVKdSxHwyPs^x36(REq!mz! z8>!Z^IkwM^wdcS=wq@S#!`3?fMy(#ri6JUNz3sKWCFt&yex@5y>N~K@IlJZ6V3X73 z8>N1#biY;WZW3ePGIk+dI&{n`?n|wXdo?FzB@CJ~Q2{&};R!Dzma1u$bpW5`fVYu#3hOwiUJpy#w<-6WCV;0lnqY<` zXe}#q*?ZB}?9tf~_uV&sR<~q$z6Pc}&2L$ccTDawu10Qrgux0J6YR!$HhTCa&m}ydNF6WaC z(R7akrkk)xuBOE^w6IF^1D?`h z(I}cD=;}fGJyWR>s&1SDGCvXX>cjD<%S!KGxM_1b{gt_Mrp+Yu z#;%ExL(m$`IFP9It>kk%h>$b}xDB>f^F)ES@8N3wp%VE467mjDn`yk3iT#7NPn z;5IVXR#qO!VswVez^gpJT;=ySZjV7)b;MXOcRB+ua;;fcSb0sZ*tB9=+Wl(Jp6rV+ z%jOYw)cRw{mrHv#Pa{ubE;6CkzSiXN)XQwaFT=s=1meJ;Ep9po^?`BY_TTZEzB)=$NKpL@3;vGsq>S9c4QZqYBHcEZ9c<>K401) z{BCu6SN8Mj@a7o2`!IsZhj7F}UDd#z6oww)tT=KZF;2gf7|E{GA=4gog$vzo+J5l| zf(Ns=C8!f>?TI4NZcCQkE%ZfabmtJnYw!bVZFi?70MrM~A>L7aJbXGBGQAj=Qiw^E zpe$p9c`A|x*R7Un1U_|uOXJ4{fnQiZOT0I{pL)q_;j-fOy>dD>O8-H!! zHh9-ic+@%UYil30Fz8eKYZ&<7PYFP)+fg%$INZ~Rp0=KZ^MAt`rPAX5C!8>}F!~WAs2h5!?)MOufMx$>Gd$oq&H?KceyVD4IAZsdNrjjVD{j$W$F|6ax5 zN>6Rf&XNfqlREmW@#r>LNuyL2TgbSTwwyEIsEFn!je*WPUPFf1rNke&#e9PZxv`p0 z@y4i+i7*L>6CjeLCr>PpU`SXZDiBe+ks#3)%E4)_9VXrSGn`oOOFQj_d+5IxoI!y2 z!#H`()_iZg3WpX;rBe!m{EjW8N;0lbSC@1u+Bd9I3M}kSZU!XT0my$;Gv3Lq0gCg` zMnLf)Ne1Cf%+_Eq8(hof{glQAgCG3gdEg(m_?ftWw}D?$0s#^Hb6F+%-`eQEswz!L zAC;pPesbmv?hG9QFd>W;#0V33ZY%^?$@n#d_<9V4J2K}aoea{Q4q>DVHGDO#E3s|c z5nL-v)D*CoNZaj}R<){{mX_9)=a%Q5-jbWY*8Sb#ZpI`@2SV28^Ri>ldGVcn!>ehBY z>QA>YA6-JJlp(Ev?N+UWctERTSKg4{%AYODd$%J;cI?9C62BeF*>DJCRSvX#ISnLP zo;>eYCQF$RWZ;eQm<0_c#)@0I5O2^WVrRe>IrO{e{3Ph7X0H%mU3?q$2)R0i4=OTa zg=|B*-HLd#h>$!3I5w90s;$*ddr?7!D*A9SHI(RJIqN^n<66Lzw ztW}D5p*6!&%ZDeep*6KpEt)uYWJRV=va?*0C9{|XFjJRJs|X?XlC7(FdOS6gZQ>12 z`EuxHM~kpFEY-9_&cadx43>yDbzTq{HS-cBpS06zHY1<(O;?m`ipX;5d-(ftAI;nPe)j?!(-7}v&!zmP%Bj7i@~jBdR+ z7K05s2=oX;Td&Rt^(o`(QELO9opv~)>5UK}@Xa4uVwJXMKtfy8ti`f%%yqCRn@?e3 zoeUpKP2-efKVUUNb`%wV=1^XjO!_5eSkE5wTeob^fep<-w9+tNHtStJO|V}>;+Xd~ z(G=RB9iYXyln^u6p$R-mjxAO?M&r%3>xqki{bU_T%3*uUrVuhJW#%n#Cy^aGuhWF4 z0SSi{OUtb{F)ichp%^NnTuEvcP+u*?Fijs=&Aq%tDL=8%4zaMl3439cHl6@g76&P< zFfP_Itpkbqe;9kmC|$Z_UAtDI=uV?m2{xMj_2Ff|G(rG=7gi|!L z*F28JDpjJmUD54UP_$C>kim#}`@v(r94s=S%@DUHq24wY%Y)+mu;DQ@8Src(exhbB zTLJ^97|N;R3)2WoRr=FP_K0Lxyk6+fO-A+8!!b@0Zj%npngK&htPT)$OLSxNbJ?@#jkI$U-a8{Zodq^Qq_FCZ?oCd@`2N5Ml~;!gwHJyIJ8y;|D0q^g_0Z zF_|22Z6TM>UFy-uS|gZ^2%MX&&kF2*CGFF+^z}lA8c<7jD`s~> zgBD*CO{dFmEoo|+_MeLTPnXeB@?zUgW~c&n_Hm2fH4a-ia}`=oZBp{_qz!W5mF&)f zD?eq^7W0tiIQ4DB=QOAr%47l;7k36Ejc!25YM+(vEM&Iw^l2pn$8Hr}TwNLp#A&#U zt&Q!=GbeHS%S+uC7^Mr3j@=VCi*?JH`TZ$BB7RlPDt==EmS*`%oM60V3WHA&5+AP{ z^}Hldil_l2r{*3gFN)5PH!E+Q-`x|dk9Z(`1b913ZehIgM|F3xYI*QqZv2${(20{4-r86b*@b|Yl4{5v$pXz!Ti^ID=MY4)AWz zAiu;8@b0iEzXG=U)v6Ui%idAHln(y9;LPV#HAYKx9c5FzJ0@X{CjnO0!Qw@XjRjI0Rl20snZEc<;5<59i;9_`w(RQ{yd3R?Gg=lRiS+bSxE z3$oR#$VXDlW7$SGW?GV1C_-ax5qH!c-ww~e5JGe)_6kyHoNo(U6qT0XJeZ|*>QM%p zDaKTUQ|thATUvz#DH>{>jm>xpqnh_)d~BWi{Bz!TN~}Dy!Qt{DMOHG?_*4syl(nuF z&4oDj#!yB2TaF2iV?VTGRV8QlN~UGB-3|R<{b7JJ?elxAr~Mq5jUZlY7Ht**SHEY! zN!eI>ntTOmk($D1dZ&9HO(t}kh9({>E|iEV-di%oc3=rwHwihBwPklAcuyjE!FuTT zH6qv8iGzpv$MQ?+TjJ>6*(vLW?ZBKv1fKe)5Czc;`bDiB@4fS?`3i4;L~f^bg{Rg7 zg+sj5B?;2t0evnQEtX+KM4R`ku~<*2ywdFXzSu=ZLFRF_rEEWuxMZ>$W{db^KBF%l z&oW`%)Yloo#I?FhoFv=$43REEi|Xtf^jcrk(2a3%CwEIS0{_|)uCXDl)@`J)P^+Fl zxCZ(p_ecMeww^pd=4qoEcaQ?@E;&o#`~hh9WHNt;xhCUUOdlE^(vZ{WU;zedlg^u( zqr$bgZVhm>r&Z#ei{Gbn2HaELLcvb%JWK5TD+zY`MOzL^alkkO zn?xKP6B=tA&tkEJauJivDDl`FyCa~O!k=|HYm15ZU?NMsmQ0|uy$-Pqa^@g+qbd7? z+#J8@w?$`MvW&Fzw%UNbUrSVZdge_KB03|;EwK0nt&RPw&82K2_vDOB0P%QpZx!=L z$1qGt^^oJoT8=MyT$$y|+cj82>+|zQYkK`O^EcOwE0IY`-LcK+EPv2awTtIUvPVP6 zll`1Pw8f=Vcc_A^iUc_{_d4WYS09l?jow1<7_;K(UE#ccT%g@J!;rh(WN7k=xgQ82 zn^BEti_yWvd87;`s$!ECw%^jfjF!XBJqxi0V@=Bt;jMD0VdRZCaT=va)|UX=nodWe zQ@EPVCLfOpKc)}&Kc04>v1$50FYJ-FUl!=x5tc54sG1b zarK3?bUa`vT@3!T7ubTAwxNQ1^=nB)QTA72=2y%r+!YlWQ3u5t6KFxKQ<-rp3);W4 z=2&81XLP74sGiXgOOQ03Svnz=gP1mG#dv(sv*y?k{3BF2)t% z@n~?Mt>%AMmJi34OXnMV23d@oyS++~wy44&HmAug*?|OgOyOf~6U%0bH&Ft42s<6| zef)H#J@l!7G5&xoFp~Nf?XAYaUvH6naNZ;Uxj>m!_(|w9{WRXe>_dnH^&0jbUd1U` zyl@C|2Wl4eWHSm!333N^d`~0rv-Qws<>KgLNC%D*NQu!ZX@MExA`reUl5|>y-w;d7 z4Pm0_f;pNtDA#2=bsp9+;DmeD9LRZ=Wd0|p^PI60MNTo0nfzKnrrb4RMJJfZX#Sl! z#D?TktZu?&$^&OBo`_(--U84)enARV_hNPqIc!8p^2jc#W0f^NLjY{0iZW`OGD85U)sx&296;^J zYn%v=`dMk+qFia+nWL&yI^msw3YC@Q7Ck+?lagu3bs$0Co_3269gh$zAiS3f|EU$S z1#rG)HslNEO=)l=0Uj4E=GLnN@|)3ubvE8*GM25!C+dUMXh^(CK~?}mw3O{SL9HAs z&$#;5kLni#X#JbeOq;#pC`49Mv9bI5qm;>74t6%32jbB`)i2_?l4TQl>=aV3D zTh-W)6V&P3(sE}e&xfd#g_;P9e+0HQkFi4%VXqZ(K7MhDktu@k2#MDIqO|+0h-^K% zK6$!MqUtq9u8@40fwS1{$cT8#$m%9k(O+rHxP*c2~*$8Fxv{UX9Ywb~C85>i@M zk#Ba0nN%O65puFgzcMegyhEhfvMuUg5|`4kip1fp;uAd zc>%h|uxG{vn&;-Xe40BYurSBUbGBym`M}qv7sDb}Y%u5<)c866r?hhM3lIe?v3CM? z-v4v)$7&sj`%lE)K$rR4VA~(CHHce}02v)2gJ{P;)jlw-_~>TFwwr@ZW~qGh@Yba~ z5_SBOEzQY3VswI&@p{+sW};_Ymw$Qo1wD#lO!)}&)$;I#ZuVbgHU-&EYqB;t?lV#~ z+S?2ycmLAZi6U|u$uEGB^K|^FKLilycv-APy z;8xC@wOQLoDQYvO=!NT>;5T86;=--8ms|Z%k)a+cz1`q|rIW~n2B_=5p9r%LJNkgV zei5tIGY`@y$bR!h2z`&J))ArS214qILUGPQ{GM!1fQ*)l>aPu9(jnR9yS#^h8#m3P z<_J3eC8@b@jNwR=ue)+M8e9H3U*$@`E)IW=lP9xs)rxK8Ea|~Y%!B3DlAs@v3A;}8 zN3qsSfdWtdn310$=YE193}~0fnrIO8q)vcQ9ssywHWB<4bOf=P>}if2?0F=|!Rf0u zJ>Z1)r-cPeR*?$0F}c)H?P`?_QhIcT>QO7mZUzaIF3W z$Y>l+;Dl)U6=>iDPv8Wur0)}4124bog^Aod!s}Hwm6#cA?E>r`YuSaXX=TfxvEaD_ zgXC+PR3a($#3 zl#aI)Smp8atcp@QK>EKVzG5L`F}aOZs)$KlXUmv+2qFi*Ahd^QA2Zi?yL$L1cUP4V zuU8I&-z><6y)i1iqEvG3`0l%GoZ7);V<*36wiHct;LgO#Zu-Vcf_Z|qC^&wG^9hZ> zNle;Y24`k&tji9GTpoh_S`O9TNZp$}jNfoKP2&2EGN~V(&aj;MXdfZKL0xuWN9(;f z*~TH}I5}0x4bu4Ii|>D?*8YU*o#lNaDJjvvum5{$P4pi~$A7XG|0=WmuhIoDFQg@m zFTNP>+ZXN_7?A)1@NEzkbny^rF`PBu9-#IR0tk-%KGawz5l)7*g;*zI>($xR25Hwh zr?`g14R(kVsq{;ytB%=@r{}J=dTPnwn`*x8lg+2j5AUnD&Y}1(_h$xx^nO$nF1tTr z6KH@j)|A(yUCG_}d$>N_l7Txk2O!%;dpwmZ2H%vZhtxsf z%G9V@)}w=w+qa_ON=gR6l#S>Kx=0Uh30k<%hZ69jORo3iTYRK?I8dVZ`Y3t*qRXDq zd8>Aj+yr~bP`o4tSh-7g=bj_*vJ;uJ@AmE>JMsCt$lybryml^>0MJrcd zUftMHzIUzXBf7P-(XoLO?D+)a^!Nx4+!}R4c5f#o8xizYX~fL1N+`EzLaLCIRTFft zwh9l967$Cuy1~W8xxw^BsfGLvjEf7jZu+1;2Sj*aEkCrRQAkf;`ydAQYXMol!xf z%Z3NDK)#p#CnR+#my%f>jkau5oNnS!t?#MBWnsYB z5wRkx$U{R!d%0V$fZZjKvN$FMQ)UEuCB-hfhnAkb%Ad)8w^fI3;)SS!{MuMvA_L)} zO6s%;Ue&P)XEd33z`N=+v>7dq+{ZN&_9tWUoXSabdB?1Kf<*OKoNJ=NdGG+iW<_P_J?PD%u_tzY=6a)P#Y zo-jXs`6!>`gZTF7q{>pE8Z~7jozM0#Kf}7rpSeDB2?DuTG6tDg)S4xG_*mYdzEIT; zcRFR%lMNYk*rWSg?u-acPKhdhOpb`dObuMjhJU4AG?|Bax3VmzNnqHk%op^((V?{h zG~y1c))y60+ZcJh(rmD*fYyI#mM0El$)(Thm0b{qurbAG zN!pRALLr|ZK`0|nbiy1P@Umzp9A%cLr9_r54gE=&6GADi6h089mKTayn;?_E*Ezez z^fH?)qc+nV%adRMj8ZFwgeLCoEoxpwniP%#$hCSe+}j{X1iFD>N*F-Mz>|nsY$_x= zVKl7F)<22BnZ~@A3!c7?z+)U>u~cZ0Zc=D|6>BKcKie-+DvY!`FR1T~xMkYdDxLRIdX;7(F9Qu%_vdwsFXDLmtkCrZHu^u8E zEfN_k$83-LDIHcaC(Ki2=>AAf7onA&O1fbMC;ir+xI`RjEhn5$${G^c9_44%m(~pY zhsq$n9q-R9#1Rc?oJYMCTy2WG1HHYW-%^fit9eCbdA95x4Xd^{-4iz4q23Iaw&)SL zWC!itECbI%8n*OMWPmflLY>!vlpQl?n#`?1B^7yF;NWnQs*xn~%(}x`BZZHO_79jD4X(dJJ7Vd7rM?-RrYq1gMn`3(zu?v0#_S0 zSYsAF`ow`;yvVJl(nd%B9&^p&NL1pE$|T3S&YqohmNUH47bR!IX_LmX$W}9~R#K#L zZpRBdOq+u(XbU!|nkE`rtvw=?G)qdvdy|Ve(E(f+~}uqxfZ?hwo}-RMq|3 zkeHA`ku^A|#nH(H|F%t20L9d9d83Fcvv>_X{vxYPH^j9GttuOjG+W0h00{PZ%V4xK;At7Sy%w#OB}Shxq(M2%ky#eh3D!Y8koB3ca}5{eD%0;?(Dd zyJx9_GuOaj>D~gmprgNYs*mNLgV8`GUW2`VH1he+LN{R8r2;2QUjL?0im9t>Ay*?d zM+5PO!2nJRCzD^O07elEFP4)|7{H(&$srw%FP!+y@!2ExbEvOqG3q0xwNUZ5=tE9| z1CuPO@w3!BAXbsaXAWHG6ZisNl-k~#lA)&s$; zMZv1Ul3tcA*7ldapFl+q$m%t2hFP-%OS>Vo8p6;Dbu?@AX1jb2b`!nW=IJ%vL;q0s zgAjfp0lJ09i!oFa1=?`hdBi$97K4QC$7N;H9w*w!F4G>hX#O58^E^esw$|xMU zfPUco9Un17p&7-`*7f4y*oRF}4f0-DNLDPzwGLmB&i_Gtyorf6p8}L-FUElav??D6 z+iFy7J&zcSVv~nh(-pNMQhG~r4%t3GQLa#D>GYX3F;+F1aZcIz0a({eGM2Y6P+p5N zsR#6B4b2SFi=oB%Tl(d^)p2>jXyqpHvaT(gt31EUmX` zXAnZmK8i67hz7%|3r&)Q-mdUU{v2Ird73Y@ob)=vv5K)0srj{nraZ2*WqrYj#2DZs z00x26*8-Z+8J0e@YUhF{PgZidcC{m}Tl@MHTwG75 zr`!cG726xraXfFf@R!tn=6M4-L8P(NS1Pocxh~eoMz1#QlQr$rEd_LpIR=T89z9>Q z&FGH8r2|5{1>)Jj*S~=!flEC$w7$#5<-Y|*|BIOD??_kK(bz%E*4kLg*4EP5?(bkX zQc+VD^BYhi605!Vpin{H*kqvzthu6bp*+_=Ru-xRjt?Pg6bMm8+h(M0^{(1MY+*4w z#CtEw!qcZfHSFIu(lX)N>3BFke#OW43s9}M%ujxN#4T@|B1=4k0$fi=MC&;eUZe(p zr^PP@pab4RUPQr6x>piH5;|V<_3>4JQg?Nv%C_)+RzPMd$z?9vDah`0nVp4stfpLJ z(owptb(DT1lIq*LMPyB&NqTz3>YL5gVrA)jg7jH9>~;k%Jwfp@H=C>$J;mnoWs%bZ zvt~k)L=;G+U$I#RuTNkFqoR4U8~0J7S5dsX&~M|U!UPGA+LvoAuu!5d@wSO{Oln&f zAK)@Kx1G(qjoQnkIa_!)uOgVDN0PnMq(wDZYh=*hYOGke7Vc*hX&xH^a-8)hGS@)4ZVawHSMC<> zI$xc14^{3K4F`SaiVfy`-6i&Vos!%UC3jh|bA#EZm*|PHM1Xk}L4exjO@i2AGvY>v zpJ^X+-0Uq8WHwPnPrVsh#{*!`pS2f^emUehKS;gtD(`{w5fWw|hcbX}-W$Im>Vy1U zT!l}N$}SWak31vBGz$cUrl+eJLq4KM#U2Gltc>SN!A*?Q%Z%F?FRNj6&)nyod`x%w z;pY*^A6o+28Q`BB!ztAJ462^86i6rxO{fy$0v#*lTl6BIr=0?DF_;nzwNn9g)~?Gky8ig8wgO`p>PH zkgbi0xv8^*{(p0HBIB)Pkp$p}zSfQw{dxU-^YREU2r_cEnIPbl8I++>#Jq&6?2?EM zqSm!pZWPT48Eyjcy5Wp7Iy7@C*816)>Y47cKQ6T1-lnE>0S>RpgC$c&^jO2OBcLzs z`GzLA*`8Y5yC(!O!_B#krXQ{OuFqB2YP5s(YwXQ_F~ST%MpRBb6hjAYzJ{G>LKl`4 z8**Cbi?bBdJasVn0|n)7D&2Kw=&D({M5E8@p6{6re<2t)xW3{BlY~gV4BCQT4G(zYT2rdn96eZvT`4k*;my* z%HO#3AT=de_gaHoq-}4Ps~oYNdUH^$^{5T5Pb!q3nWHS;qpkWzaHR#8eLa?mh_=O< z3a=oP{xRJl`n9DHrGzKLCpvk>Bb;c~ToNKQcj4i=2$g>IOQ~^N{Sy-g1$stD*M_#5 z<+M$6;`Q*dJd({B#($UzI^!3?C)EuLa?Fz4ZqayT&<~iOo9qwSx`|bsy1OE0)2mSQ zKR$y26hdx7kq4eHa_PWzkvoaKIPQV;GP2LnV0OTP zf6>41YD&ZuKr`k8w^V|PkK#KPq+S6jz~jeD#8g1nK~LJ3!FasGa6`1kdl&HylQHn%dOaWFPCHn(&7muIpJbHRS2 zkpZSVR0Ya*-xHwKm(|p^0cFlZ#H2MBD2{@EY(9@JdSbCj^cZ1jMd?wkSVRaZobxP- z=^>1?^0y+h9n{m@TV)#SZN;l>|7ZXOn^_5eP22PmTU&fvVZs|3_XzsZmVaiG#-+Nv zT@@*72KV{5SN(pw|I_;88~E^#_8R|eondHY{y%zQ{Pks`GbZP8-%cUpTk7<0pvS*% z7qYds{$|}sncEo4={tNcU&OL@e>nxkZ_JjB>EA0@qT+=N(l=ab4z4MoQF}C*Jk3ve zmByH2mn>uu6N%^t5;W)+%!OK#m23LOz;XUF@Moeh{!BmEb#8=f5tZN?@U!F5CYR$S z&(Y*R7O@{CyDRurISHhOMX>_vL5>tkvnj!a`UEgA#^z`&>? zEsZ>%v6`^1>5C@EYc`Il`rVenO-FI{25m0Qqx72RJWJ^m)(pfJIzseDOIE5CSASdr zzLKuk1zm1W*H?BsY4*rd!CfzW1-9g1xQxlY4qAJwIk>@rFElK(wi?oSZP|OvxSEvj zG-xhajl$X1;R1BoBguZG7J=(73#{#AFS&=mMxaW51QZEY?^(JH0|YIh&ARhum!3|-4 z7#l?mZJ<+X=2l^dK?RMpQJ8(TCL{FBnWd*Z1S7>z6Bc!rGJBnn>p*b|J^1yC#pRiY z%Q&9F$K)~glfx!I9p?mA@G`iEi)-B&{mikY@acZky^Urabmqb}hYZEMsPu4ZFNpi2 z8Tw-pT{>Ym4v^O{P zpvABswskwrFFlipjA@!4XM(>kBJKCRO|EZqY_IkDiN45gYpS_vigfub!#Jg8e?e;; z`o<-X)=x|}^T62GfAI}}nYb<-?`;0JiBo|6Kbp9J$+y(|Z##GV+r%pqt!3xF6@FiT zu+EM_vB*QFp#1ou5_0oG805XcM}(1_LfaW!EpZmASX_VJ5It*q5rj(Qh5oeya{npM zuZ#6#koK73cv|1$INEev%kJv@0FWCD14Ws`tV=l|6IW#@Cx~>1Aw^Y6P^MAYncNZ>ejld;X(x9?QtQ4XrYRFn;B#cG7d~VQIP_G|dg$Jy=ImZ>9B+XefgO%P zN+kIfmVeUYuZyh{fk443V6+=Ob*~mdV9vY|SeR_>Z6qONfp%9b0G#qa^Y@7%sATmR;HLx7FlT@|CKRp!3tj*{es!RvV8>?>D) zxAM9Aq_RZ3M?3qZOnhhX~&Jg326FbIcWSjJa zZ%ng^6NeVZPco2WCmBj;=-4H5%&CF{xan>XOiAXvltQ1J-yCLL{~{Os%jO9^Ml|BS zU3%)bWbofu_y6YB|F-jgCf-#v9sdFwB%qJg{zQcC-@(QAHAGrR1xzTA1OLM>=35{x zl=HJzSyU-jOZ{;6t>Xi#{l4;`5UxS7w_x1qwO`2emF!h$^_QrM(Mp|UQGDoR)-n;SnsE~ag!WegIEuu zG!ziwjhZ|y^^0L9=pZV{bE6*28L|^4--_&kFC(#EM23?T5g+tKG%DXePEt>rX89%B zQGQ4nWwT$zY^o+Wl%T_`Tx?*8__)}GQ)&aVKz&$*t;rVNN9Hale_>!O&XjoqMuNKv zUxUdq?~qEGj_FK+I2~yis491SD`Jt)`E=Gr;(M{aRsjnf>Yr^*Dx}RcR zw`5{juDQ=Lta#`<8==cqIHhv1 zm%~m}lB<@&c*U-Z?zkGKCthk+n@1E6q%H`eL945I?X+_ToR-N*O;R@It*2nyR0+bI z_Gxe-WN7fxlDseaf?tjyrxg`Aj6o-m3 z2_Y&ZHUX?-Ol5wOK74#m0o2%HQbil4%iUd@|JEycoS(5?ezRqMr!@S;*3(^fhTQ?s z=4h3pG$ie-i~6rY5=8fyTLOz;qFjVpbjDeNqufGk>xvD00>pPri#!4iMX~i%A>z#r zb|5&2xCU5(&HZ2Tb`x^%Um&;#y5 z290*%aA=yK*zK#-5AM{%bXB{Q=MYFydf?_+8EleU!vvHn~`v z&L0=GmNAaub?EuPTMDra%@mcYfS|H#Y@hFLI=SL{63qaf%_Sz3h6J8Z{ zOR1PDxhGGxSq^>elf~eU5t#aOu`40{7CBA)IYQi7P z@>3((I=!n*PIV{RdlfoQj^ygKw1$1uOZ$!m^<&G=IJ+yCoyJya6Qpe6uD#?ZkR!NL ztPk)Vkxd;{BaBV+09p7XJt~9C{j`@JE5X{-)4yB{VlZ&^v#Jpe)jzcdIkd>!5A9;R z1nEFVtKA_6?804+v;Z@PKAqWIBVf z-2=qlqG5#7A?MrHs-FrEe=YyP(|As`K0R`{+mFlLKTKVpKpnxkj+1|g!;4?21yq)JSytE_(8My$9eLgJN}Od;ZT>o1^~}T2@;o$`rt)_sb+1eS)4Vbt_*AlvIgD|94|)Js z>!41HC*@?IF_fv*MC`E$}VkZO!Bo?B3;XtH=?}J`}I<` z7~2F~8~xJtP8r779u_1pvlMnEz1m5aad<%_>S;_4I8=q7)MBdV_t zbkqo=a_!$>i-j5pzxz=~C+L+Yl8^s@>IFvNEx1E>5qc*|5PU;t>*Zx1(GiNe{FP1Z zP`dTsd-8u+tFCc$(YKWze_#LBX7S%*kbiD1|A`9;j1c$>7xMGVtWg8X<)AghQfNvJ z8Te-pQollmZ!n^wR;wNF9Uq>_=GliE;4pkJxR7wbv6W5JwsmDn&9L;E zg!ZN71erO`nlsT=+&7?E5JUCsw0mPAfxsl7ur{%zf*K`UaAEhzJrRLF#8snyBGD-d zhu|buP+U87uW%=isNhRX{FDR8%k&sz3oyb96x}oXHS!(ifhN`K-*(*@R$5cV-?-`` zp#Q*C|KGp$@38Cd5C4WsIT`*xE&2lO_)u9s2G zN#8(Ga9pr;(0QrSYzBi-nWr`AAA~1hp-zg#W{i6-!L2(P;nV?>^#l2IOY^I}0?W>k z0R5fEkzutB`%Lf+#`Wpy^+p9jHo`1oKk}gowP1k~cREA?wzzMO>{H9>3U7YvC|4|J zpH~I>{GkZ+L<=lW0>F)aB5MdFC-nm&M_jZ>qGzSfNZrV0k7&tU0DjT?T~fx-q!iq1XkWeCR< zfkmOf9kW+udSo5#)(h@1T9_AS%w+V@cu{?FX~Gr~k6zmIaDExf`Z5k{UgNO_roTbzro=wfX=9UD50wOIcAG$@x zAJvana4Ln5Q;#R3k4cuKp4UkdkQPCs5O1Oaqp&H1bwf)II`|$ILpaF^gSmgN1{nBA zEJQv+py5Y1D49E76-?@?m1g-Rl=krl(pbL`+`r_-Bm^L4qjOBRS$g6`>aY$PY*If*51a zir%@`IJF0*@fMoXJ(d7W%lbJMdTarhmim(lqU6Putn>lye>yKRU@>Tn?=dy~D{=6j zWBR`jVWhmYWFJ4g_uS&(e03Sw6d^ODNVPPDm^?BdB*f;QlU6Hnwz`dkPgb~30H1_r z>0ID=LA0by_xh`E%GuG=8CEyXB#skK24V)WB2*Pj?iN(BRnL0@#ZDJ`)e8H80E8!w zth)hVH6#LO^jLroW@>4RKa(Gpcy7uF3)%Vt1e}DLQG*g8I;cIi+aqF@r+!f&9dmlY zx~#%U7X!~xygX|<0}DjqYD@hYP62CYRpwwY^3OF@6oXI&v*T`0vU^bbE(`98OofC4 z&K+6C##c0wmKhHSt*t7xfU%3o^y+|1AE@35mtB96fF@R5s%p0YTn{jgPP#Gyd^d`|I6~{M3PPk1n zu<+VMgnZ00VbGdFH=)pBOf*oBL>txMPW)q~NU@w=B(E&3saR>8uONR<$kt|c3`VRp z0oHYl7>XLX`5yz4aaFi zlxDPOQi-ajSPLSZ0qj`5H*@TF3NB?P5w3;7 zjaY^rJVl7a!tqzKHciFLh(Dfm!OQ47z1eA5tW0z2bCOJI?n?9!uqH&lfiY$)Pjnh^ zse9YQx?3q;8>Z-~dW0EXX^GuZg>oU>MU@{tO?G!INWF0^ZvaScf(4GE+V^{ctcxcJg%qFZ-{Xvgb%8-A#kRY5^T%?|Wr5Z$F%Vg)!+gKt<%|&iT8TQWNu0Ueo^;TWxnrSGXOXs z9gGa$+^MO%Ea#Pem2rc_s?I5$h<8^{{a&VRclriG2~r)*^P&OF^O6BG3KshE=aMA= zU}HS6Q&{H_0%0_EkJFq6(r+fnndsO}aOE8O3zK%Y7}TdvGWRFN>~M!J7-&{}9Q_de zk68qxpzIn!eU0=huIAqb9qG;>MQemZP~i)0Bc=oGP?F7P^`M`~ zL6S3{9WI_7?rraYf*-Ke+azwZEsuUnqKZ3Y4g!kXY!d}{rRP)JCgva<=_OJ-q|bUW zKO*kI0B1jpTgR)HUZ@M!9jN!x2yUDV&E4KJ@Y=u5|4UixUyf_vV1aDsdr`3Y&L;Ey zr{nsUh2fu3t+I`!&3FII|0Uq-P##nG>+pnUgjk=E9~}NBFob*r9(*U4FodwMP#IYx zuugA@SaI*zC@4)TLHAl$OAIBN6784o*Y@C2ZzL95YfU>(DkOp+iT%#u=H=Q`MyL1X z#Qw+CpDTcUdrbl8JxEUa8G-T)`uN`cfu00E^KoH&mM}|bhn2l7+yQK%cLdk5);3Z= z=u@Dz#SP=|tPU%Ph-PP@Dm%V6~u~{Bd5{+Nf;&Sdcl^j-Q}B-WjzV0-gh=ix|CQGO=N2s1)Ui zvqhJ@hv`YQI{V1Yjg#7n=l*)jo)&RpK(gmYO?R`unh#Q=K9VzN66_FDvbKzmH@$Uq z93mye|L`cVWv=AZ7%B#xRi3VG#>IFtW}5$z4Ku`>-89d!S&LxM*&}io-K=wBFknOP zzCPt+u5owSw5@C4p^(x^q0leF5YhNCH@27&ra66;n)gv^$bqyQN$D{|Hr^g)L#%X< zUn(e&9HtK~Qk%0&ikja%K>su~*F(R6yaq5oho59EYV^9piWa8NykWrQ;P z%Epsc_edr1@gs7u@y9|f6@xHdFYK#YPz*9muChYI@Y6iWErM8{H*@Vr1Jw+zRHv>X zSWH7LPe8*tZtvEvy3klkgPQBpg0~H4cRZFg!lRiBe^hzC`{bbP=FTL$No`aOJ_PhyaFc znsAxhKIBkCR139t!^vG{W%p2SL<1`md!CB-f6Ge;7B--nA_4#mepiJ5|L3Luo<0A} zORGb3OS`R;v6I50S5^yfUyZ_uk;Oy1IDS)Liql^<49?)I8~WOidk^f+c<2+dJp+ ztnOG`J-fMTblrS^Zb1@e4wpTgg?;sTdH>0aH*!>s`9YKXDT8;mpR34up*LF^s=kMv z>13|WiE+NT(x;QU$oy!Ykv_zEbfVN9B=f>8xEITba#O-RdF01UyU(=Gq_Ibs344u> z7rkK49X+>PJx7dtwa0Yu1pFd}945}G-52kzMNMQnVj319^>D*%HZte!)ilWD1W6yh zj~FTGTxRfSpW1M63o#r9`Jx2rPr1XT$XhQg+i2s0o89NQub6YAm5J)uIC$rzF?z?M zlVcw#Gv<~n)7UN+f@;|=TzoeGDe2rQXM%dyg1L0+R7lJsU)EXJYB zi+1cqP#v@R6H_-s^vta=`i#XU=8_VjWp&6>_)5}ioxE$w!mCt96@1TG3%}!GH_Z1Q z5L5SXk91`TG*iPp|DN^5tzPZOA@k2Q3TJxz!q7w>#QRKM{XiwJP5g*Uq`y?ZQ3ZH# zrJ43{#6Si5T#9n$jUMqjdNgRCwR3Eg43otmk$mJI2I(p$!^JSWamXRRn{64Y^bm?I zoy<-fuDNE(zO}wsZGBINMvO@HnZag^Kg_TKy933kXL^)&=&(#E3t)x}Ax);1`PLSY zo~722n)05^MfL&a4VWB!=#c|hZB2sJuuSWKpkoz_V)LVzOU+EizN^|M^P@50+{0t& z-M=E8EEzIuEgn`;$}yXm=NIX=>RMecRu7Vy>0eZBm|WA8wQuxFC@mV*+saka?afeh zhM6f1sM;#HMhulOojX<_($)v{G?yb>f7bRLq}GR@XA>~3s~9t>H8Ubwms^CV(GZlE zaUfSnoLd{yTdj=JY5`_}n2;l{DC4bLHxiEz^kdZkv1IMgjUzXyWy+VYD0TWGlfz-H z*7?U$vNZ5w_S36nGe`?klqF@MMWkv9p;BLJBSVAjNv3F#6rQE5JIA6*vO+atwsJOU z%rpvu%Tp0en3!c^z>Hq?=WuP}24E`k>f1J%3VVr5R0l{9((!r5jLcC;rW__(VOtH% zTFHm`NT%`I4O`+qL-fE}0c4K3vT2U^_TwHV7syxolGgR@gfb=cLijLDV9a99ff~a# zyH(mjF@j#|{GJXY-czN%%IWkCFhQE=T@@2-8bgNV=rJFzX$Vnz6Ii5^f-$v0vR_K= zU;z8WX|1CQh2*ZoalgpK#zV?f*2)zhd$11^To9L_&SABx@9ucZ4+{7jF&4QT25x_^ zAxQ z2Xtm4ri37eEclAwiwpXfCTVHb0z=oCNsYO4F*HuAL30dy%^sK2L-Bi@PfcR*2u&r# z*52C0$aQInLz#lBKPtG{N&81m#F{M5Sm_bg72|6YsOpTnQ5dL{hu!|TG&Y6F4#FTd zNKJ$^t5D!EPCfky3LiBxl+1J`j_+HaFP=PBqC7QmtVao#PUmtYox6|A705vb;h(%r z1)@3{8+8n-caWQjGpglR+b8)ou24pe&f6z25NL~Vd?6@k8{zabUA_+@DjK6dPXQg> z32noATA+0Rbh}3?U>zEwM)x3Re+op3iC-{V+Xg4>?U*J4;5L4Iq8h4WlnvW>EJhd#C`w{z0 zL)|-a@C~{%sd~<>t0(*54CWJi3m{`GipJk|A1K5>4AP%(itW43jDor26jhyV551&h z+3jU_;sA837Yw>Hu}kmT`u(TB?GYD%8HuD}Aw<|fqA}>!2w31Tt^d`b5P&rc-5jH2 zaKE{GXl>X;qOsT*GrP1(`dI#q*!*^1-u-fqIu>-Jm>|=mGn~)Jw&x44{}tN*SJ_uU zRkd|((~WdD(jna--7TF;hop3OH%NDPcXue=NQb0!H~;ay-d8Wz_kQ=^W1MjgL)Y`H zy;ttJ=6t3gcZUP!Jt43DakuNhD%~Ux1aDV_scX+_IaIwFHYBT-tB3NMj%t&3#D<2RwFoMXZw;VowBD2SQ>?4 zBEuFOjLh!XUPu(IJMrh#J`usd>1#&tulr<87WKwt8)sKH)CLvu13TT2tq*rAch^EY zsu|{2x33|5?rxl*63bf7K|m;#HECesUK5$H_hE&xxXbUfdl8eCih$17B@&@FYvHk% zDEP6P(z$%=WyIJkmQB6N<&{&xD(1M#cBhQO>&5m1J|&(Y}fGe`U0Qwa$Q*M zw3To`R$6fsT7zkm2PNR$V~|=D{6k4KUyR^I8xL&zfD2Z!FG+S;tuNiAdYN42w~N9< z2Fsp_FgQ3`aqfK5ZMaE!u*3S1Wt55jnWr2JWj`9mcw@Dx!qzI0ml(9EgAeU~mANeE+%`A+iP^7s zhk53vFCy`vo5I(>O#%>NoR*t|_F@L)LXqCu0J6r=jP8KyExRLX!=gnWcFB_UMTSq) zIG!@ENc=}mw3Qn?!z<;+R?&@53;Oawf@;nD%jEX9iJ7YC{1aM)stg0BPc72SDdWQj z>x+x3voos1Ss@b2>~ZTaNp>ddDmrywpN0+=PErl^1rjjTT4#rF`)v4TssqsB>Q$RJ zwK~FROA!gmt(KF@2qj3VCmG3SUDdxh1u;?ES1u;C<~5yzXyH1g=c&hd^WuvyElJ`k z5qvT1mA`;1C|^KR7(iw|^DnW|XwmWmcsXN0vk7&HLvB%j{>mB`;$FsDI5c7(%#+_C z0}87&(P)+SGGRU|s%+$fKT0Fv1^nq(B5O9iogrBvWArMLQ3;l9zlwRe9h6nb-7G!9 z!T=xdP~3WgcOr(w8RlbWC8{>e39bjPlR*TUr>KM)R>B{RdXl++D z)4#G&g(cq)LnTb;_Ik?~liDGmfbDeo&V*Vi)t||VIR~@{kEzx8Bn1vgZsa=(r5cw_MGN$z*5BBh1DzmZZ;1t<39~ zCqNqlVr1(6M<5l&+0eFW$n<@N(TIet%{xSabD{{7@eD@qQ?m;`41SK79KgrEPz)|r z*{!DvQ?dwFdc`9Uq5BARTXj&r`qX`n+2~vNjqrRF6vu&glkGx*U*Svj*S&0kJZ;YG zc#+vF1$hNEPZtv@K_`N+-s%1WhpTb+JVDnt&hB}ZW7pWSek+rLM)>RG_i0_wJj^GE zn^%)LWNMs^Yfvj8YwP%&-w?wfb5}suDS4P{`@UUWjxi(d19#dA%k5r$r8~^5E^cKt zK%y{&D&3mBzus&UD5dSpf~_b}q~(+xZ{mS|Q*q(aLfQ05v~Zsu&8rD_DuA*)9pqTC zH$)XA3%lHG;piayGp^7O=GvyTnBx3+GxRb`Qq~mm$2+RqLYPoP^pU;HF9O`n0V7fN zreUK;-!eG@3dW8kEij74fU(jRRtraHKcmGc@d}+)Qr}0r4Ht|vsqI0>IR|ep`ad+f zew`W=HKw%vB(B27;NhExkQD*ge*-7>oifVMKLa?mj~d1iZ4dC&e}C zD|tBApiNO8vTSDryULH$OML{B2OQCl$ag$q6&Cv4D!cEmm&Idtt(`$yMI+4^^Z1#% z)BIksrb3t~?0h>X*~Jwc;q^mn95XFWy5P64$WoCgpPbB?%%fRG?gDnZHq+5AY=I{lC$srho`453`HGxDM4l%-s+6^-)$Xj?nvAxV%ypsK1*HSZQ`ANR0SR;`!&yhrPm4vLI_)vG#%>?*dyeQp+-d>~GrJ&A zur;DOqIe8zP4b7OnYJ6M-i?^GQ26IyM;+?%O8G@87Cb!$3e0zDt8hfcXYC(niT$_Yb@RiO4%6j zl(Ka_6lT(NRF`jA)DbsxDGT8g!__&i?YKwgyB3$P zK;%dYBFRUF0lhB@6ED#GMQn8%3Kqo#MQ?e=@ ziH5B%;y3TV(rQ*$l|x|JtLJQkRNX_R)k1XSzL`)y z_aY;D+k7ZdXeEoz;~8T0saIVyI^U|;g-=-2{sQVXYoM&fOEMe$_sqUPUR71 zm*afYT?c|OxWn7=5^~XRG<6pc7U$tJ<9~|)R3*Aw4;^V9V4y!s{`ngyd0a!$lJH>= z!gv{9l#6|sC{Z!Fe8}}(VQparZ4^*i-^SFkB)=ki^w)*xvxVyIh&N(1ljDMG-Ox#5 zti$eXKzDua>Wd_~Ms#*Fkf_%=8&^5Oid4<9+J}6|z{{|3xOCIZAS=A%#6v!!-!$LKKAEwsH%sr z1uSNn6qPZ#WKN7I&TEQ@AcB2OzYcJpKbDZV1(u+P;tFduln3czWG#hpX|T-$jBA1} zX!AVASKj?bW7?(n4L+BRc1`F9c9`kD_Pq0>S*`eI!p6J-mk>2L!|dyMBxs8K}P>ke70P zQl!2O0&vJ!4N+E(KOc@%XwAHigA@cC(?a4Gu>D>Mje{laF;-=lN~n2fF9~FUmMQIu ztc#f}*{4gVTW+`8mGn{`BSU*YX2psi-n;GOv(%%-Hk-SV!``Pf4`4p7<=9Gahq4S( zqHGjBWGO5n)ll)%QzvaRZCq{5JXvu}1U-Q+B&^xh0yuC7hI2pqHdQaWwLvM{da3pH zt_2qoSEW8@SDsK81G8>7#x^g@(@*vKt~e_?T}S>WJbjCAy^b~@Tkhq$BCfC533v%N zF!4Vg>tWedbX`MxkUKe1gr=Wf>0m{y8cE%q!Q6vfNnVo{J1KUR1JY5;l>oB6Zh5}P z*$VWH@%<9-muX&ZvS%+VV1v7aWPDJ)A2oxd5kh`GquOPBP}$eV%(QHjaO*y7Z`(|k z>=D4%SkAw?H&}7YEu@8%EQu&2^vtCaIT~mwCWSuOq;SRTVzJ}p75Lbt~)`taaQ#D$TTW#P{ zt&{Zd^^&UsZq}y;BAE@_AzaQ*BW9SWP#3D!r!#0$6Y1DoISY_Ag3w(;wogD*RI+Wem#H)9RbU?y7GjutCrHVB)Jy-(!8bSY@%}zU7OSC z%V)QD(7F`wK$Sm-@3LX0Y<)X5R%bVcxy(@Mgl~w-BnO)r3aDDBtIb|Cy55VpXG%d0WHM`FWRRcI1wIjM9%!sa7*T;^!tXC`V*B%RWX0eq+6u1I=zKT4lNSi=PE&&6&mu z$DWO;!+)9HGi^xh3|cPcsL-iFb8mjH)U^UOr5jtcioA@}a0z;8>J?5}7mk}Z4Q@3& zxW^A7J!nFBo`*r_=GC;`%4p0yHE%+jZ-zNpFlkA5EP}VMp=x(-hUVy81k-9s!=0J@ z@~|MkI9@w`5zBu}ox9DGtJGgR6^ch>Dae;4$|+)}G7ip&4dccDrMo@9qmz{V<SE4rlTd+GwX154>%e!JXIz``MoXyfG!3l69-dGXmw-(bh71p8H3njf>(3zeaa)80Ol z5*Y%~(+KESmMOhS$Z$~^wfRi-a|8H@vi!)hF&FujYAO8jj_9rHB@RAzZI?svw((fRyOZ85Y=QiSWU$x9kG>S3Jl$j{3>uSh^ct@cPF~<&zA|o&q@Xq6Jp?z$#LpN80Va-9SxjK^$ zEZdm6dm@obL+G+Yg$RUIeW{&S(kfQzfmR38+Ts_pZmx+Of1KtB z`Gj=zhUW2*qa|d~BjtmALRA*()jJf6UjFC@eDF0UZT}}9s^0#nGoKFIam;JAMF@H5 zB2ikXO6e++7rHf-g^iqwTCAYfhWrTxCpCvByNqgutBBL}#~G?}Ixy?}ew1TAFbY-y zDmrjs@20*%`sc6FzuqHN%6y`1=mwdD8e_j)wo{_BANc&4=ngI#+BCPzE=h7G7i#KoRQk z!-z!3cta<*>z;%^8AXCZHYuhMKA7I}BommLW{&@tK=!6k31&Hj=MyuwN`=-UxqLY6&UWh4aJ5H%B%hoPt!U z%rZSCDHWa_^69V(HQ;z$#_BJaAaw}GTTvE8iLfBF4x6TML__Qw*%XL_Q9xL540K^E z-99ylW{l6=Mc9f+2RPSpM0z{bRn@txx=>KsSV_HOGk8@2CaFvVf32wM_$e#+qWzD4 z05`tR(7$y{`lShfobLZ8R!qd?gN?0%o$il+{^QWHSNYqUNjbzv84ai$UfI@9F@~nN%X#$Qb_?_KRW}{x(Z)~WxA!G ze)^l)W4RxxHf?3d6)l5f^leesSQq%21`c)biK6zWn4a{B>JRSXtcq=PFg%M|{&hX? z6&T2rvfmljDF;WwO@!uAiZIe)z`&@5vEO2{zX(`AH$!} zgBD|~nQw7Jqm=G){*tU7v+_BrrDT&BYf`ROvqf`ad*2C*^=36L3|CeY3*U`nsfs*b zLO>G3V7jf<90lVz5*Y5A(dk-hcZgc=nlr|xWGI~voR|6tLm-yh-~d$&Re3@0r&` zjHy=3m4gZuDU=8>H4c5<%v3MMLXa54b~D9Jobh6Wl1xx`H&U#;gi_7s$*fSU10<~I zWo9d{MN0}lSKLUVda^@Zp1cb6{E-4&+8&zra0~mzqsm?vIlnN*!=}a#PlxO4*JFV; z-M!m5!{1ovr6-rA6=D2ecca1iIjlX3xnRtjm~U~`1#(`3X~*$He&Q4Z$hJp>c=nF# z6JvFwH4TeActSV^l6j}E-h#+pQ}1bBd)?kVrXK?`w7iJQBO0(DZ>WZ}PFy(XY{rF& z5!+i)VLiZYSl{;C=TQFwTT^WSHb*^}n&CF?#RaV?%GTJ~h||+Zu|omU09_xEeU7dr zrC@xY^R9R3gtX_XnD!!)x5n>i9gC{Nq2OZ}2IXx!0(W#DfQ*#+IiIt@Swj6x|T}r$Vz6_#J3Xhj zCGejfC1pPZG-m3Wj^ltA7A<`+y>E=f!))%@^VXlB_tgOh%{NuS4LFeegnb(|ZG~42 zLnc#PmQ2Q$pmveaKf65BFe)7A$*fY}m|Sf}SKoc<7_N55@x(|oAfP~nZ|VpZ&5hk9 z?o>9;$TQCDhQyot6*G)2FuLF0J>>t!`2i$)Co2vb;XMnsD%xL4qTeaWUm6ji-Dg z^3k3z&k-uuF?3*tDC;fK-F|lY7}W|9#Y+*6mo<|h+-15`+XUUUk;PaaXwlk<+&!1| zNb0Kgh@yEz-Xd}dAYi!>_$!dF?I2Ac-FsgaYFQ^Rdz%(2W~X2G7Yg8`;7!NRPxn*e zpA>(yjec5>zW7M92AR(ly`l4AEE2bui~&vEQ3T0EC5GfobSG@p&VhO>l8obJn;Zeo z>8B-TLsu+YBf-8m6x2B9tI^(|1(gW;_j>VtH!s4z)OB(Y#6CTXW7crANcadaNc1E9 z)_3M_qu@tGfyx&T92K-jk%fT?p-QFx@Fg;(N%lAc4u6s{$HI^eGHKieN^5Z|br_WQ z#Ilh_AIArRG$bT5$NX}0!IR?Lm_odAn4!sd%s>+XSvb)|PVYCs8YMpgX+!J}VQfoSt)wFz3y;@#`E&_szN`iVvYJu_xJ4a>rGYyNwp#^3!#=?}6bLK(dDq z`c5--w%G%zg&}*}gA9pN zr!et3BXv8ro-Q)>gSN4jXMs^@%1r%06CR4hp;T8BY&dtQoI8G9VyLj$*_R6B^CB{r zHc3*w6H>xN#Y_^fK6qMq-6p&Cg^@%#)J`i-9cyQJ*q*;dg!seZQ? zB)_r0wgruiT%uYE?LHsb|Kdm{#-&TQ6AQFN=DPRUFzoV@(4WMXd7?tIOx~KE&*S{? zFsLRJ%s@}lNVspkPL2Bd((J7q@<&M}fOC3SS56^pmQM_UEu->Dq=8BS)M2rGqp9SM zDW_FVQ9MkNSZRq$_^z^_`UFSRmyBZ&YFpdECa=djnu1t$K(-0i_vhouGvL$3jguDn zQH1z7Y;o_&o2%@=xpd+r=3Bb!*;`xn(erCaOTS(jiB?qkYOV%IgAhQ~Vd363!V3X{ zJDnkG(OB+w{2Qaolw=?Dzs%M&iUTc733T#vXU7%Ilbu&NOc3hm&1DbeVt$oUlS`k?%IBtF8KFy#u;Ser3^VVkZh5^ZSpqc_^Ux5_A) zf{%mn>+IF|YqS87a&Jm0>I9&h2Nq?6dy!6ZhWyp~Ydle>rOSRw*a z%VZOm(%)3?}Yna4K(Dfaw?;>1mvUM;};KqWn~~IN27kU ztyIKL$~*il$#(QX^~3E@qy}VaDIPbKFfItanT(NJ@E9QgOjq+#rn zcUE;+El%WPK6Buc>WV=OsAF}q?(_qS}1nrFl;F(h7>pEHk@Q=)*LhGwW;K-Bnc3XRZPh}ghV6J@erVWH2sc=%D(J8 z-V)9Ru$FxPK}2%%I+W;hwG!<6@G;6))Uh+F+#NZ>@9v(mIh#KLv&tZ1%Y6KhqL z4Pe{*TH9}qv@0LBIHZpFGIVVNQ@GkQeZ{*kUEwc`;T?8h{OZCGbVnbeRZ)pUx9;BJ z3#Uycp3QBY0{lg6O(&GdKYu(#^qAqXKY?_eJsi<8`WnUYqdk6Ce`+}qKXCXu=Bii( zl5iLFXPA}4ksR4|afuXM*JuG(Z)IN@kEWNG)XzJDxEv%q!f|NvMx}WAW8EZfDR%ZL zs4^dnl32*N(ZP%=AtL~}cqT+}8DDlOqz_Hzs77LKf0~pM2;AvE0W*P}MWU%0xduzm z?cv`?a`omS<~MJAOdd!<+h@Fm6i~e|kz$AGN)^TJ=UfDBLopw^!dxRsxdWPWq~9me zkzEGA6zAUD+>>qt2XzBmP?#lJs(oRS{;84)za`jM-wx0S-No)OoMKr8`4(>~14#CB z%UjP?6Go6~l3zgkWEn8IFJ0aop06pCT-`RIaiw3HJiw7^+iTyWW%>Gr8w+wavk7gh z=bw3Wk1XsYN*;x-XqFJ)bcaVz5H zS;AEUwrv7vR+czxols#JX%>D4juIJP+srd)Ri~v0&ioCc2?natg2S zI7_*syFNTvr*NC2%mZ_2SC$fN8x#!BCKe=xv^nwk>pSNg0cgAe!0z@60tkrv@4MSy z_O(CWIi*c(Yyj#zf9!JkKN$TIKbRV;B|G^JAXGBS+En^YKZCH15#wVkpy0W9LP;=9jAC-lAdZJncn>u=OORVrzLy9D%C`vT(jQ%Fn?>a98Nv0(As@ z7BNIshDsFa{3Z=;Dho3Q`K}8&F>6(|-NVurIByVMygY>UnZR?>XAdVTdVN}_1z)7T zpC&9SOGT=*VOeiCVK(a_Dqpo3e~(6&q^TH!QzmUO*huS7Uti4F{*n$WI?kBi%#P32 z^K$mVmvn~K8k1_Aab;Sj(E(u+o6sPLET%b2pw`GHSzgl!sQawVe|ahYE3L@RaA->` zCRzAKSB8bE%VoIxXk-YZbC03=rC>MVn^V?u{$6N!Sj@?aKpgCmwHANUP*MJpo5#yQ zouYVjrlHGaK7XkzdwMB+J$CnOJ%5`9!6LR}Eroa;!zv!S(7jsS@;umhqNQOhlUvVT zA{7%=86OgGA`T)>EaHRLCPdC;r~5#eu5097cRBt8xcc~HqYjwwjxwtd*u{;q+c;#R zz+|IUMP^Mx!N+oeioU|y3AM2}!hC(>VYW^IAMVY>{>|E05@bX$SQd*g@yW{y57e)a z^W%|ugDn250~}VXPeA0Pu{nsJFzE3;Eg_)$NE1GU)(P^mZDLyTZG_=YHlSvpbMx@Y zhmSti%f=q=!|LuA2TZyO62yZ@>kxY`5)D?YRDf1I)ah%pDa|cb3~F!W+JeVu3#*MX ziL!uV2^q7K8YA7HQ0su5VkmbBPcWSeQC&U@wi6sj-`WFMU0eadVE<|a2pAYzelYkS zW*2h>6cto=iz+MYYGX0@*t$6}A3kj;{$f5vSfohgAoGu`bM?}ylhgXMU(AX;GI=wf zx<+bz;tDS{jBazEvU#&6Z5;HV$SOLm^WGi~HjeUaH;$&cTs~eLS<(UJSikL48%oVG z)E2?C!NtNtMmL~YRR@x67&!?_ps^`|nuEt;ROXn0s2H(U=!1A2%Md6Xo)U^!$9{@i zk9B)+bm464uR~*`9;?{<#-UifNOM|2ty)gDgjlFvx64)y|Nuqg=mngb`D(Ea%*Ddvg{wh6v8nOM1w(0>hI zQmrwLDNp^h2zmj@qNEfiQS?ruN&b_qdR|MR!!_9p_vs?2UC#UQdL}&Xpsz0$!IQQS zq!n5rEI9B@W2ja_tufZ-5UB&eLKWw>^?To_*_)fgLMWwq|>MZy|(Qz;|` z;^H25NWnIurE~{+>$D772M{?CWA$z|!_=LC_s7Eo&*=m@iWjWnx9KKah1$+K8Ye`A zz53*MSU?JnK~JXYHi*c}*-@orGR54j?e=CSDg9Ak(jX@h{S(z1vBiimS>HF5Nw`j* z`=NMzV}s-kE^sOdV%@~GqU|Ww2=+0nEAX}Wz3y1HV#rp+4}hganf8D_GaKDd*1dCA z+bREBWzdE7GY==5VEnf?2v<&@!=@`PO!wk$#ZX5DA*l4xh7B9$ zWG06{s<^@lSEgSm~_)dz&@_8#e!9%|zA446r-P;j5DmdOEx^n%OtE9VG9Hj~4A z?Vw%{KRR^T{mv(*VK>M0`AOX{EkObe0Z*-dNPV0q& zqOElZg_yDxJm==U?p{Y&wq$ir%d@0(!lzT|Ho{q1K zi152%8-yF^FVe+dyGlQ>Mc)(rbrq#WGxmlAtfI|;pi;(vvx@$)nEtYM#wx5>PROA? z%21-h+H@=`GNZ8J(zEWlGGR)SBqcAqGg6kR7pF}n)Ov|wHBb~ z>Kr32vFF*v*ABfayQR2&?IN-Cj_@8OliOT2#n5)k&v&s>F0aAvkgix z9zkP?FDxYrS#ZS;qHWvO`$Tmhvn=wfQvSq(Zt4pTN6Xf?VP7;$Kp2^ynyC~B`1f$9oL3bI@H<$RmqA$D#z;Knis}40jlY@Wq^F}4J)uZfxODnqHyrV_UWX+n#92@vz^ZvG zd7!XfYDmMnIPGT}X9~}0TK8bBfVHe7UeKdEx~$lQ=iVTi(u#&}>@ zd|fiGFQ+u|>nUpbIZHZoS_X1HPuNDiRlETf>Ikasm=W)>rNE@nqP(RN%cSyX|uFRh!e%g5~mg|0vxG5bDWL z2jUhyrs;TPhTgcRKWS|OXPi=!nS6h6xRSpY?e4(4WTLoq-gjf4EtYgK3&r}Kr@712 zmvQ84DH+wR-*>%6OjEq0C;$(bFcLdT1)H$D1eN6st8#HA=0*Q|8gv}R5z4FE8T?;n_rgHik@ca)VLfg0q0D+C_mZgx+#_dGl?$KZT3=fDe6Xx zbHjf+IM@v%BSxDD>0WRa7OH>wVYx)}eT@oFl_`HAR~Gik)u4)t5JxOg?kpx^+_$@% z-smRZ#U~&v|4V~zBz}dUstdZJVZQk-0@C^>zq!0OeQgWprZcdG7A#BC7UFmkgkJg} zvXy0eK5I^I!wuFnn!_zQ+TgwxPV?YTx*-;AWP_1K8j5Lc4_8=sn>4*Cp+)a@q^rfm zzifw2!d&=(-Klf!Z;Wkto7&09Bj6*lj?SY7uL&*LtTUgeZ=#O)wgK!(>P0_PDJa!| zf>>0#F{l;JhMbM?sZanfk%;xXd@HYnOW>wUBy}^FlcS}kI36E{%+!}78eeQS(}>Dy z+XP$&Um?*UI%iCqxTZe}c8D?z^+#@QBy6G#oQb2}pej3&k<`psU?aU*Cdm_s=4uec zC;w19u=;9zkT}5Qt4SdqaaveldyZ77*@w(yyj$XjSM+mxHy2Fi@AB26DP#hMqdz7q z)3dP1uSr*NN5@>mF)cyZ!t@os3qNJxesNNr0Beq~r_Y2+AOM{Gm4`57ig4*2ux#Yb zGaZY%jtZLuKwmEcyomgN^mTp*oew|ML4TP|OO*hspz?@3^#F|y`%1J-_f<$;q$X4t zmVta2WCjxWq$%c3kNN|3jQZxS!Of(H3ug%f2RoEk>CZ1&Q-Q9&& zjt#D5T^VLgcBZVPpj@Sf)2OnJ(|H4OpGvKpyZlHW2nz=6u^1z?Z{xn1wQLc~QQs}* zx3nFw=grkLZYTO`yf|Q7NDq2^-}GrkS^Cu1BTxr3k?PmqpYJ@yV+D`5o3b8f|ac0f@31z;N~X4PFOyWcrjnw zt1uEt^h9S=xQ-8sN!G2x#kHcn)Ub&MZ%<=3iv{7-`nVcK=jgXUdqy{?xSP8tpPzW9 zK78J?--WBS7=)rJ+?Yf6I2g;b`^D_4AT{khH*S9DIL@1V2P3fV54~Qt>aTc&v*e5; z{Ee%&2dI&VC0Na+j(o88sfk703C3mlX$#^`X%NDO340A01S;V+3;g$mGag7-hp=($=W@K+<^NYUG7Cn6iXe_~P6I_wg7Z z~A z_lJ2sYFGl40o70TmMOcoPTib(uj{Zz334{pJJ6fHp(6Lr7CSF0QHYUg?|max!!fWc z1%tRG8m+h5;@7mryCsy<$CF!#Rk&efm^ntB1>q%OpAL>~?|dHb+e{Bxf=B?mtIS-2gXD+K;1@2e-^mvI)Mg0$~G+&(DuS+A}^2-fKV)^L+=2n6qd+SO2+1JmagmK@aR}KSOWC1V{vHfGK{IH1pvFHLUC04%*o5d>q zN43@6Vs>1f6Vp=R7+eSr$I`%)5DqCt7afB>k?2GZYnWzNeZsyceGOqP4mEt|L5@c}xHNR0Uy+lx6_2l0px95LsJ7H7aWT z4pm!Rb>r9$S{ND!S)0k$95ZgsoRQ?ijT=OzKEWQG8(Q~-Lvk4C(oG3oLJn5J9LwCk3a;4Fn@+S-2_u*S#UiiGJqgkR%O!~Ixt6DTCqgBc^pIKPUGAReW zFjgXnzZJ99(8S~vlRZE( z$<$O?tJFDMXF9|yj8LJGbz`|RO8YE0726$VZK2DqXsnMG99r?kwQZ#uHLBAz*%65T zP8qhHg?Jic{Z8;pyiP}7gX`?AyYEqltaovEuZ~FEW4C9WbI5hV|Bt!)%u+#L($^wOO_@bGx0}p%NX(WiJ68D5{&3MW(zCIQ9Vp z_BlUXm$3IEijD>H)Zqq8=cAI}xlbL4t|=QzA+iQOf#niCC2x8~bTDFHFw+a0bKT}* zA;j(DIv+d}0*1jrF_sF}8PSAGA0Xd&4i)hbQ?d#zzIeuwrW@*-zoWZB5|n7pxm0~@ zOYljcMND>7Gs`G0jfo;ZN(C3ev-F*b6e7o2u6VRB+%=|O&-)q}oe@>rkt*N};RKQ{ z+%G5tch@FE*WYOH$2j?&q!cKtK7PTdA&wyqli;nAzvVAf?cI5HA|5$#9>)zBAqs$P z>2Kz(Kjm|Nu^m2hIf_;ML-Y?6HiWt^(7beI3}G|dcHwoRfEd)cu|N^hL=WIlZr1!= zJe***N7qUg9rJh(!D~f=VrMuo z-e~NVfD;Fv6COC8u)kyPg{GQ8;MQqAochv{mH)K=x04v1@?qgYQwL~qHrIu{H1#f} zJLrN>qlccEQ^$UKoLzgijV2CS)id~FI$x>|ubks%<@N1WxV8m8$Q#Zz3|-Vdr628( z3B@WGYm%}q;lG6IZ|qC7vq;n$v>B>L%~Bm?nrptqsyE;u@D~dq#hs|C3-m4%pLqXj znulf}4B+3g>aFu$yJM(x1(E@^b?0L&RtcrwHx92l3KM;LZMRCNw@qzqW0n0@#k-to zqygS#`|nIxvpz%G5>S zzD1M0=EfioUhEj0qeDi+D4(nh2|M1JE76461{1 z800%_>Kd|sU{&GZ{Uu4UB%17zQu(y3I}zoV%{Q=75g37Ks1h%Y{oYvn&MiZo98JPd z>+?H7mrT09EKtPUmZ74wOkJ+%XqJ`r%~Ejm4ER_)5(Ul9O1_QR&zsc8n|DqA^}R25 zZ#AE@e_CaHukyS4r)QHj!c(^jDqxZZs5$@<06ssY_W`nZ|17(|`K#DIFbFCrAXgw1 z6i^Ys-vfa1Uk!VJBi4@#5$J&b`%h_MMSfaw84-Xu=3k4ktJ(Q~{~yYL&-WVrdF6P0 zSMd68F=>7oaS>q!#aGfIzanHjBL>(Jeq7&)Qvkm$KN$Um^LYt?_41bz9f058OZ*Vu z|0gOygu##NJJq+p7W|RJ;BU?1!G?>0X;q z?*Nu@>=%DHE&K_Yl?_bcFc*T(0W zWqtz6>HY6KGtW`Jm-^ltzh8jS_~ZJ%nA`zSerfXWVzkc-KTiSkXYm{_D*e9|{$)q` zmv^r}44!}QLf`N4c><1~1n@@xg5dAzIG*3Z^K1t{34l%iiQrd1(dU@YwYGm^&RYHx z=8vcHuavj{#`#P!`6s8}hkwEOo4M-y$CTt=djQ9NPog+IsOgy&)2JeBkB8`=(&F2 zPa+MszY+aX{~!J8pVb7PlRcMP`$?AQ`G1rBel$Oii~Wfy6!U*$lKfMY?DOhB53u=3 zWtjXos^@!`U!L9n(e}@SK7P`0r2dWO7uX+7^1nks{&qjl{MdgIjb{9f=s)QH%d7o4 z@^jzdpUBgIeb4jJ_dS!>ugJe|=;wY(KQT*-|AzVZ$MO$eO3!Qm-2dYzQ+e6HVEPAd zkmpR#PlS+Hz47-s$#aX@Pm;Cvzmfbo!T-{$|BpH3xfSFm iyjj \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..e95643d6a --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/manifest.yml b/manifest.yml new file mode 100644 index 000000000..7f06f91ee --- /dev/null +++ b/manifest.yml @@ -0,0 +1,7 @@ +--- +applications: +- name: pal-tracker + path: build/libs/pal-tracker.jar + random-route: true + env: + WELCOME_MESSAGE: Hello from Cloud Foundry \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..05c90d22c --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "pal-tracker" diff --git a/src/main/java/io/pivotal/pal/tracker/EnvController.java b/src/main/java/io/pivotal/pal/tracker/EnvController.java new file mode 100644 index 000000000..f38e4a944 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/EnvController.java @@ -0,0 +1,44 @@ +package io.pivotal.pal.tracker; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController + +public class EnvController { + + private String port; + private String memLimit; + private String instanceId; + private String instanceAddr; + + public EnvController(@Value("${PORT:NOT SET}") String port, + @Value("${MEMORY_LIMIT:NOT SET}") String memLimit, + @Value("${CF_INSTANCE_INDEX:NOT SET}")String instanceId, + @Value("${CF_INSTANCE_ADDR:NOT SET}") String instanceAddr ) { + this.port = port; + this.memLimit = memLimit; + this.instanceAddr = instanceAddr; + this.instanceId = instanceId; + } + + @RequestMapping("/env") + //@GetMapping + public Map getEnv(){ + + Map envHash = new HashMap(); + + envHash.put("PORT",this.port); + envHash.put("MEMORY_LIMIT",this.memLimit); + envHash.put("CF_INSTANCE_INDEX",this.instanceId); + envHash.put("CF_INSTANCE_ADDR",this.instanceAddr); + + return envHash; + } +} diff --git a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java index 31e8d24c8..6fca475ea 100644 --- a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java +++ b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java @@ -1,14 +1,30 @@ package io.pivotal.pal.tracker; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class WelcomeController { + private String env; + + + public WelcomeController(@Value("${WELCOME_MESSAGE}") String env) { + this.env = env; + //System.out.println(env); + + } + @GetMapping("/") + + public String sayHello() { - return "hello"; + //this.env = env; + + return this.env; } } From 130a2093d07960038e4f5005d81a951ee4474538 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 10:54:36 -0700 Subject: [PATCH 05/25] Updated with concource pipeline --- ci/build.yml | 23 ++++++++++++++++ ci/pipeline.yml | 31 ++++++++++++++++++++++ ci/variables.example.yml | 57 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 ci/build.yml create mode 100644 ci/pipeline.yml create mode 100644 ci/variables.example.yml diff --git a/ci/build.yml b/ci/build.yml new file mode 100644 index 000000000..d6378d09a --- /dev/null +++ b/ci/build.yml @@ -0,0 +1,23 @@ +platform: linux + +image_resource: + type: docker-image + source: + repository: openjdk + tag: '8-jdk' + +inputs: + - name: pal-tracker + +outputs: + - name: build-output + +run: + path: bash + args: + - -exc + - | + cd pal-tracker + chmod +x gradlew + ./gradlew build + cp build/libs/pal-tracker.jar ../build-output diff --git a/ci/pipeline.yml b/ci/pipeline.yml new file mode 100644 index 000000000..c9d05b1ef --- /dev/null +++ b/ci/pipeline.yml @@ -0,0 +1,31 @@ +--- +resources: +- name: pal-tracker + type: git + source: + uri: {{github-repository}} + branch: master + private_key: {{github-private-key}} + +- name: deploy + type: cf + source: + api: {{cf-api-url}} + username: {{cf-username}} + password: {{cf-password}} + organization: {{cf-org}} + space: sandbox + +jobs: +- name: build-and-deploy + plan: + - get: pal-tracker + trigger: true + - task: build and test + file: pal-tracker/ci/build.yml + - put: deploy + params: + manifest: pal-tracker/manifest.yml + path: build-output/pal-tracker.jar + environment_variables: + WELCOME_MESSAGE: "Hello from Concourse" diff --git a/ci/variables.example.yml b/ci/variables.example.yml new file mode 100644 index 000000000..fbac2ee80 --- /dev/null +++ b/ci/variables.example.yml @@ -0,0 +1,57 @@ +cf-api-url: CF_API_URL +cf-username: CF_USERNAME +cf-password: CF_PASSWORD +cf-org: CF_ORG +github-repository: git@github.com:GITHUB_USERNAME/pal-tracker.git +github-private-key: | + -----BEGIN RSA PRIVATE KEY----- + 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 + -----END RSA PRIVATE KEY----- \ No newline at end of file From ffdcbeb6f7bab52b92a71cd20818116301cc2eeb Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 11:19:56 -0700 Subject: [PATCH 06/25] updated welcome message --- ci/variables.example.yml | 50 +--------------------------------------- manifest.yml | 4 +--- 2 files changed, 2 insertions(+), 52 deletions(-) diff --git a/ci/variables.example.yml b/ci/variables.example.yml index fbac2ee80..a8c790b22 100644 --- a/ci/variables.example.yml +++ b/ci/variables.example.yml @@ -5,53 +5,5 @@ cf-org: CF_ORG github-repository: git@github.com:GITHUB_USERNAME/pal-tracker.git github-private-key: | -----BEGIN RSA PRIVATE KEY----- - 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 + private key -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/manifest.yml b/manifest.yml index 7f06f91ee..9b7fd344e 100644 --- a/manifest.yml +++ b/manifest.yml @@ -2,6 +2,4 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - random-route: true - env: - WELCOME_MESSAGE: Hello from Cloud Foundry \ No newline at end of file + random-route: true \ No newline at end of file From 399e427a6a13b692133b3a9a0cd65a4855974d32 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 11:44:13 -0700 Subject: [PATCH 07/25] updated buildpack in manifest --- manifest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manifest.yml b/manifest.yml index 9b7fd344e..956cab586 100644 --- a/manifest.yml +++ b/manifest.yml @@ -2,4 +2,5 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - random-route: true \ No newline at end of file + random-route: true + buildpack: java_buildpack_offline \ No newline at end of file From e96b7eb82c09d228835cde24bb4f57fe8fee952a Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 12:10:39 -0700 Subject: [PATCH 08/25] update review and stage env in concur --- ci/build.yml | 4 +- ci/pipeline.yml | 66 ++++++++++++++++-- ci/variables.example.yml | 3 + manifest.yml => manifest-production.yml | 3 +- manifest-review.yml | 5 ++ .../pivotal/pal/tracker/EnvController.class | Bin 0 -> 1527 bytes .../pal/tracker/PalTrackerApplication.class | Bin 0 -> 734 bytes .../pal/tracker/WelcomeController.class | Bin 0 -> 826 bytes .../pal/tracker/EnvControllerTest.class | Bin 0 -> 1469 bytes .../pal/tracker/WelcomeControllerTest.class | Bin 0 -> 1029 bytes .../pal/trackerapi/WelcomeApiTest.class | Bin 0 -> 1704 bytes 11 files changed, 70 insertions(+), 11 deletions(-) rename manifest.yml => manifest-production.yml (56%) create mode 100644 manifest-review.yml create mode 100644 out/production/classes/io/pivotal/pal/tracker/EnvController.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/PalTrackerApplication.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/WelcomeController.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/EnvControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/WelcomeControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class diff --git a/ci/build.yml b/ci/build.yml index d6378d09a..915233e7c 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -19,5 +19,5 @@ run: - | cd pal-tracker chmod +x gradlew - ./gradlew build - cp build/libs/pal-tracker.jar ../build-output + ./gradlew -P version=$(cat ../version/number) build + cp build/libs/pal-tracker-*.jar ../build-output diff --git a/ci/pipeline.yml b/ci/pipeline.yml index c9d05b1ef..c34c3b2a9 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -7,25 +7,77 @@ resources: branch: master private_key: {{github-private-key}} -- name: deploy +- name: pal-tracker-artifacts + type: s3 + source: + bucket: {{aws-bucket}} + regexp: releases/pal-tracker-(.*).jar + access_key_id: {{aws-access-key-id}} + secret_access_key: {{aws-secret-access-key}} + +- name: version + type: semver + source: + bucket: {{aws-bucket}} + key: pal-tracker/version + access_key_id: {{aws-access-key-id}} + secret_access_key: {{aws-secret-access-key}} + +- name: review-deployment + type: cf + source: + api: {{cf-api-url}} + username: {{cf-username}} + password: {{cf-password}} + organization: {{cf-org}} + space: review + +- name: production-deployment type: cf source: api: {{cf-api-url}} username: {{cf-username}} password: {{cf-password}} organization: {{cf-org}} - space: sandbox + space: production jobs: -- name: build-and-deploy +- name: build plan: - get: pal-tracker trigger: true + - get: version + params: {bump: patch} - task: build and test file: pal-tracker/ci/build.yml - - put: deploy + - put: pal-tracker-artifacts + params: + file: build-output/pal-tracker-*.jar + - put: version + params: + file: version/number + +- name: deploy-review + plan: + - get: pal-tracker + - get: pal-tracker-artifacts + trigger: true + passed: [build] + - put: review-deployment + params: + manifest: pal-tracker/manifest-review.yml + path: pal-tracker-artifacts/pal-tracker-*.jar + environment_variables: + WELCOME_MESSAGE: "Hello from the review environment" + +- name: deploy-production + plan: + - get: pal-tracker + - get: pal-tracker-artifacts + passed: [deploy-review] + - put: production-deployment params: - manifest: pal-tracker/manifest.yml - path: build-output/pal-tracker.jar + manifest: pal-tracker/manifest-production.yml + path: pal-tracker-artifacts/pal-tracker-*.jar environment_variables: - WELCOME_MESSAGE: "Hello from Concourse" + WELCOME_MESSAGE: "Hello from the production environment" \ No newline at end of file diff --git a/ci/variables.example.yml b/ci/variables.example.yml index a8c790b22..d02466efd 100644 --- a/ci/variables.example.yml +++ b/ci/variables.example.yml @@ -2,6 +2,9 @@ cf-api-url: CF_API_URL cf-username: CF_USERNAME cf-password: CF_PASSWORD cf-org: CF_ORG +aws-access-key-id: +aws-secret-access-key: +aws-bucket: github-repository: git@github.com:GITHUB_USERNAME/pal-tracker.git github-private-key: | -----BEGIN RSA PRIVATE KEY----- diff --git a/manifest.yml b/manifest-production.yml similarity index 56% rename from manifest.yml rename to manifest-production.yml index 956cab586..340b53421 100644 --- a/manifest.yml +++ b/manifest-production.yml @@ -2,5 +2,4 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - random-route: true - buildpack: java_buildpack_offline \ No newline at end of file + host: ps-pal-tracker \ No newline at end of file diff --git a/manifest-review.yml b/manifest-review.yml new file mode 100644 index 000000000..cd3361d30 --- /dev/null +++ b/manifest-review.yml @@ -0,0 +1,5 @@ +--- +applications: +- name: pal-tracker + path: build/libs/pal-tracker.jar + host: ps-pal-tracker-review \ No newline at end of file diff --git a/out/production/classes/io/pivotal/pal/tracker/EnvController.class b/out/production/classes/io/pivotal/pal/tracker/EnvController.class new file mode 100644 index 0000000000000000000000000000000000000000..b81a0096159e0da3d5d1ee82cbc0210ba8a6eb5d GIT binary patch literal 1527 zcmbVMZBG+H5Pr73z5o@lDhh(4R)z9%z91shDk(J*3z)V+jUULiUF%|dSMRnZhM4%t z-=m2QQ4@cFKgu|J6nlst)DOEm&&!O~MbxHPK zX}OA9WwBaeh#i3CO2uIar=%rSn&HvJ(J23?ncSqzGIm8U4CJIG)|>T`aEiQCBTYPK zmw9cIJ5uA0(oip@%Wx$pZL=X;w&FFjK|?vb{7N`x)@o&JOF4F}CLF5mV!ml9Sr?nq zl@#SMcX(YW;VfGg`6_8!6m2eNJ5|$dXg*%(4Li2;$}9g*&2TmZVkjvzHgIo5EF;bYuVe875^;SFd0hmG2tFa1yB)1~3@IaXp>TQyhcDWS1&U9%V&0Qg<_gyD_A3kDh_Q zmE>`xe++|uoo*UK;%_b}d3N99Mrm7==?w-a^s_kDP0ULw*l3b%!Tu5Tc z!+Yf&6)w;>(NDjKfq*80q=4qaTZsv8C0e|d5nrdsK1Apptsz=Rl6zpusWt-1={ADN zVWSNrInzeyqvvy4@1ZXUI74R)qlZvG&QhdNLSv-WKbRMZ@CYHjUuVKRN#_#qB%Mvr zlXN}?#QIWs}7w0itw{NL~>K{Xz~*RpV1S< zlc{(wf#3n?BM>?OF#<*he4q#cbc?)%C@vF8SLmdxFfoA?l3tNR`=@n^jA9-M+DTuQ e7vU=H1QEk9Nn`9em`i>EOnKI6&l)Dn4Db`c?rZY^ literal 0 HcmV?d00001 diff --git a/out/production/classes/io/pivotal/pal/tracker/PalTrackerApplication.class b/out/production/classes/io/pivotal/pal/tracker/PalTrackerApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..c0f84793874df0866c34051e62be7e24c19a2a45 GIT binary patch literal 734 zcmb7C!A=`75Pc3I8@6eIK+}fy!YxDSr!+L`qm&&+#cKRrJ@0{D)vA*$F7@G(RSp8|YlsQpk{xjw^M=U~iG zJu$x}!)BzkJk4hlX-8s`64H!JEYh*C%CBF+s=HEihQml1o~c{oM9MSzoE7nnwESG8 zqs8wa%Tg5!r;P4VmzhwSq1CyJuEnk3sn7`@I;(WjD_97dP~DvsVTN#MavRH^%G26g z+2YVwWoZA(wNo=WR&zz92U-(DPl0OpBV!Xj&wS6R6*GBf>75Kqv1VKY11EPqoy8U?1*b9~HVY{;7a@(PRT!aq^7(diO8bpCVi*>%+hXy+C8J y0U_QMSOf0~@ySO;F`(Y8bpK)PVwoRO#AgQhP^wX<)h6K;Y!!AJP4d3i4)6jnq{3AI literal 0 HcmV?d00001 diff --git a/out/production/classes/io/pivotal/pal/tracker/WelcomeController.class b/out/production/classes/io/pivotal/pal/tracker/WelcomeController.class new file mode 100644 index 0000000000000000000000000000000000000000..ced1743609727e37fd1120286fc2482871e55778 GIT binary patch literal 826 zcma)4O>Yx15Ph>vnl>y=2yLNIKp+lDknp7kBoI;+rRA`ps6pEU2jtzl!R^M5yiTeL z@t;5fA#vab@S_m3Nd-mep$B_DJn#8=JimVb_z7SO52`5P&N3Eow}Q(t4G2-Q&Oiz>(2P#YLP<5>F&Ssf%EaB1q*myluvCcI!y^&51 zl~-=t7{+^T4Kr><)(u2HDr6rO1*g`Xh(t+~izAtO>&}Gy3lOo)#)`Silfq%Kk3SxC zqt0Hh`@Yxh_qTVup9o92JbR_G%o0|ct@-B@HvX4(1x2SS5s5a37sc!<-;<-!tl=aG z)xI6ORPE@ZkLz?Ox4^*lJu zR*Z){N+ljGLQrtB7qDN!65C~tcGw1NtL^Wg_LFZ2zD#SXyst18s4>3IwKs8%qxD%e zLR{yFaZ^^}S|B8*uJNkpxWT5unAt6YBjSp3`zwOa>;w!1%PEqZQ{r$|a8A6+`4!Hr V@v6TA<W@?2=J1%Y=8=D|}o2sdBpBbul=WOlQy7 z6rSzuEi@v_M5yK7SiCH3*YOoBGW2Xz9AO{wE!(%L25HBkbgS14$-L|B83z@rrBO>$ z+jJ|Q#aC=uzV3!Xj7j|obfA-A;5?~QbLM7dr>Owr6OR}olT!@s;}er(KKIQ-KIRLL zb0Ha*61WU%Y3%9xMp4221Rfww8mkox<*ndh0vTi#HFLh2ZrT(+%U_wk*)%AXlR0h+v2`S zyty!MZSYdnv1#PkrDmFz$n2ZmCO@ih$KnAmjivGXXa5+&&}{p;ql#H7x(xGS6=&J~ zm9q9S`q54w9QliYnuboYU34;NWwbU8pMt%sUU!p?S3wOIX`cuja0xwhVlV7s6O9z}muCQ-CGI zSX^RKT@+H?btq(8kt8KugrrF466t$J3OVVMs`~#j`Y!UiMhp@3l9$Xa aK>7&jCnQG54cw%itib@S47bQeF!%$aGJFF7 literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/WelcomeControllerTest.class b/out/test/classes/test/pivotal/pal/tracker/WelcomeControllerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..a49636ed7b17145d6c5abfaff7a477b310365353 GIT binary patch literal 1029 zcmb7D%Wl&^6g`uqb?TV5q%D2$Xm}?nWLU64K}b;)#KHoJM5(%*m{Bspu~U0o=x4EM z5lDOhABDJMM-5T~DwaIgb06oPduIIe*S8-4o`c(1!JdViMclHH!)>$Nv9XVejk~yK z;l70j49hQ7p!6$-e6`kRD73>5l3~qNfqa`x1~Tf2flr>Y8+yX;i%6OM%vsPQ6*D|@ zwTv~Ns*_L)pHJ!4k?=msh#yMd3n#K22099TpWrC7$xu?dD^BA#()U9K+q;M})Lj)` z4U2H>&Sy_fwF-mS!b67L_er4DMD|szh-WJZh*BD6Sa-u{$j1rI%V#v~ZkR-#e67rg zdke`n#^OXcuu)>DUX>Zc^;YFmHhX0v<5&!(!Fc50F{&174(d3t@WjDWG#DEHCm_vY zSaDwjL;h|smY!zVn7b}SVc4p=bAMMyDi}7+K^J3jI#YkMTDzRXuy527aV#S}=3W>{ zE~bjNQisu-#LuF=k)ZaSMU7I$FJla1b3b%@B%-c7PGsQ843|!nFK+wS5{4BOca9U` z_vj|Q_>)DZOtbcH7PHRuQxxf!F;5OO8%ne~wBuwM*`@k9*q1bXjn?JVS;Q)RJ6QtO zuueNVd@9bT#XYh)^6h*_?x>tUN5MTf!_rqQm-A<^4(E8QlxdJ`Lfj@KgL@;Zo#C2D Y85@MZNwEUiEy~E4Y?Ec!p*4ry-!S$OL;wH) literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class new file mode 100644 index 0000000000000000000000000000000000000000..f2402b774f4ccc70b4482b3200fb43b572c7f5f8 GIT binary patch literal 1704 zcmah}ZBraY5Ps%fSU64+4^1Q*f-x$P$oPUW@nX!0q|`b?3a+5ae6Z%00fy}DTxMq> z`B_$3(XvX*&;BUOXZHvkVZD5~>Fb{Er@No-*?<21<1YZqXw^`~`~+%P7{_9O>j7@m z@DY}3kob5UpVaUvJ`1ocPmU+qzwu^qq-!cbw|!L+c*PuC-ozI?6-c zm0@Ib>}7}g`cQCFU}imyz3J&qlbCL#TXAf?^2WxAz^!6rt>Q%PsL)&YOseAm*-E`V zG)`|5cI=LcjlU~Uo?qA$sI1s+mU^yjV*McPb+zj#W)o<%ZK$G6aD{A^Q_EH8RaA_#YrE#pCBzIfJ?=il5prVwaHw!7t z`v`69^;0kNyvuSU9~cn`M1bPJlPSK49)x|v=^nKmgpo=TO}>`e#>xV=Iw75HKP>>3YQ^5HZe z4Os?(&4sfjfh$>E*~&P-FGK6J%xGxkhQYh94Q@MoiVtbEAooJP1ZOe2N%lbf%ARr? z`bnzeP!Di9a9N!8e?tT&OtShUGcS)GMDaNBErRDiLHAyKbtBb!u{E}ZccxKL^(=^WvfDU*AZ&C_epuLm_|AY$v0kez0 zqSPoiD&;>>eU7oeQE6ZQ9YJIKIVQGFP^VC)_eG+x_3_aF literal 0 HcmV?d00001 From 62361621e15c3166d2077b798802121ec78be229 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 12:31:55 -0700 Subject: [PATCH 09/25] updated build.yml --- ci/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/build.yml b/ci/build.yml index 915233e7c..ad749cd9c 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -19,5 +19,5 @@ run: - | cd pal-tracker chmod +x gradlew - ./gradlew -P version=$(cat ../version/number) build + ./gradlew -P version=$(cat ../version/build-number) build cp build/libs/pal-tracker-*.jar ../build-output From fb60f86c8a621ab95394c00b1ea3aa7523514942 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 12:41:15 -0700 Subject: [PATCH 10/25] updated build.yml --- ci/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/build.yml b/ci/build.yml index ad749cd9c..663282c49 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -8,6 +8,8 @@ image_resource: inputs: - name: pal-tracker + - name: version + outputs: - name: build-output @@ -19,5 +21,5 @@ run: - | cd pal-tracker chmod +x gradlew - ./gradlew -P version=$(cat ../version/build-number) build + ./gradlew -P version=$(cat ../version/number) build cp build/libs/pal-tracker-*.jar ../build-output From ede74df6baf595034b27f38bbf5d89f162eec044 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 13:51:36 -0700 Subject: [PATCH 11/25] updated prod manifest to scale 2 instances --- manifest-production.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manifest-production.yml b/manifest-production.yml index 340b53421..21ab1e7e7 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -2,4 +2,5 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - host: ps-pal-tracker \ No newline at end of file + host: ps-pal-tracker + instances: 2 \ No newline at end of file From 9fa47f3025627acc8d8d849abff2d696506e1f40 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 15:04:20 -0600 Subject: [PATCH 12/25] Add tests for MVC lab --- .../InMemoryTimeEntryRepositoryTest.java | 71 ++++++++++ .../pal/tracker/TimeEntryControllerTest.java | 106 +++++++++++++++ .../pal/trackerapi/TimeEntryApiTest.java | 126 ++++++++++++++++++ 3 files changed, 303 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java create mode 100644 src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java create mode 100644 src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java diff --git a/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java new file mode 100644 index 000000000..c88bb8266 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java @@ -0,0 +1,71 @@ +package test.pivotal.pal.tracker; + +import io.pivotal.pal.tracker.InMemoryTimeEntryRepository; +import io.pivotal.pal.tracker.TimeEntry; +import org.junit.Test; + +import java.time.LocalDate; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +public class InMemoryTimeEntryRepositoryTest { + @Test + public void create() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + TimeEntry createdTimeEntry = repo.create(new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8)); + + TimeEntry expected = new TimeEntry(1L, 123, 456, LocalDate.parse("2017-01-08"), 8); + assertThat(createdTimeEntry).isEqualTo(expected); + + TimeEntry readEntry = repo.find(createdTimeEntry.getId()); + assertThat(readEntry).isEqualTo(expected); + } + + @Test + public void find() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + repo.create(new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8)); + + TimeEntry expected = new TimeEntry(1L, 123, 456, LocalDate.parse("2017-01-08"), 8); + TimeEntry readEntry = repo.find(1L); + assertThat(readEntry).isEqualTo(expected); + } + + @Test + public void list() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + repo.create(new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8)); + repo.create(new TimeEntry(789, 654, LocalDate.parse("2017-01-07"), 4)); + + List expected = asList( + new TimeEntry(1L, 123, 456, LocalDate.parse("2017-01-08"), 8), + new TimeEntry(2L, 789, 654, LocalDate.parse("2017-01-07"), 4) + ); + assertThat(repo.list()).isEqualTo(expected); + } + + @Test + public void update() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + TimeEntry created = repo.create(new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8)); + + TimeEntry updatedEntry = repo.update( + created.getId(), + new TimeEntry(321, 654, LocalDate.parse("2017-01-09"), 5)); + + TimeEntry expected = new TimeEntry(created.getId(), 321, 654, LocalDate.parse("2017-01-09"), 5); + assertThat(updatedEntry).isEqualTo(expected); + assertThat(repo.find(created.getId())).isEqualTo(expected); + } + + @Test + public void delete() throws Exception { + InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); + TimeEntry created = repo.create(new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8)); + + repo.delete(created.getId()); + assertThat(repo.list()).isEmpty(); + } +} diff --git a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java new file mode 100644 index 000000000..f7c0090e3 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -0,0 +1,106 @@ +package test.pivotal.pal.tracker; + +import io.pivotal.pal.tracker.TimeEntry; +import io.pivotal.pal.tracker.TimeEntryController; +import io.pivotal.pal.tracker.TimeEntryRepository; +import org.junit.Before; +import org.junit.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.time.LocalDate; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +public class TimeEntryControllerTest { + private TimeEntryRepository timeEntryRepository; + private TimeEntryController controller; + + @Before + public void setUp() throws Exception { + timeEntryRepository = mock(TimeEntryRepository.class); + controller = new TimeEntryController(timeEntryRepository); + } + + @Test + public void testCreate() throws Exception { + TimeEntry expected = new TimeEntry(1L, 123, 456, LocalDate.parse("2017-01-08"), 8); + doReturn(expected) + .when(timeEntryRepository) + .create(any(TimeEntry.class)); + + ResponseEntity response = controller.create(new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8)); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testRead() throws Exception { + TimeEntry expected = new TimeEntry(1L, 123, 456, LocalDate.parse("2017-01-08"), 8); + doReturn(expected) + .when(timeEntryRepository) + .find(1L); + + ResponseEntity response = controller.read(1L); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testRead_NotFound() throws Exception { + doReturn(null) + .when(timeEntryRepository) + .find(1L); + + ResponseEntity response = controller.read(1L); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + public void testList() throws Exception { + List expected = asList( + new TimeEntry(1, 123, 456, LocalDate.parse("2017-01-08"), 8), + new TimeEntry(2, 789, 321, LocalDate.parse("2017-01-07"), 4) + ); + doReturn(expected).when(timeEntryRepository).list(); + + ResponseEntity> response = controller.list(); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testUpdate() throws Exception { + TimeEntry expected = new TimeEntry(1, 987, 654, LocalDate.parse("2017-01-07"), 4); + doReturn(expected) + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); + + ResponseEntity response = controller.update(1L, expected); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testUpdate_NotFound() throws Exception { + doReturn(null) + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); + + ResponseEntity response = controller.update(1L, new TimeEntry()); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + @Test + public void testDelete() throws Exception { + ResponseEntity response = controller.delete(1L); + verify(timeEntryRepository).delete(1L); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java new file mode 100644 index 000000000..b742e5447 --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -0,0 +1,126 @@ +package test.pivotal.pal.trackerapi; + +import com.jayway.jsonpath.DocumentContext; +import io.pivotal.pal.tracker.PalTrackerApplication; +import io.pivotal.pal.tracker.TimeEntry; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.LocalDate; +import java.util.Collection; + +import static com.jayway.jsonpath.JsonPath.parse; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class TimeEntryApiTest { + + @Autowired + private TestRestTemplate restTemplate; + + private TimeEntry timeEntry = new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8); + + @Test + public void testCreate() throws Exception { + ResponseEntity createResponse = restTemplate.postForEntity("/time-entries", timeEntry, String.class); + + + assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); + + DocumentContext createJson = parse(createResponse.getBody()); + assertThat(createJson.read("$.id", Long.class)).isGreaterThan(0); + assertThat(createJson.read("$.projectId", Long.class)).isEqualTo(123L); + assertThat(createJson.read("$.userId", Long.class)).isEqualTo(456L); + assertThat(createJson.read("$.date", String.class)).isEqualTo("2017-01-08"); + assertThat(createJson.read("$.hours", Long.class)).isEqualTo(8); + } + + @Test + public void testList() throws Exception { + Long id = createTimeEntry(); + + + ResponseEntity listResponse = restTemplate.getForEntity("/time-entries", String.class); + + + assertThat(listResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + + DocumentContext listJson = parse(listResponse.getBody()); + + Collection timeEntries = listJson.read("$[*]", Collection.class); + assertThat(timeEntries.size()).isEqualTo(1); + + Long readId = listJson.read("$[0].id", Long.class); + assertThat(readId).isEqualTo(id); + } + + @Test + public void testRead() throws Exception { + Long id = createTimeEntry(); + + + ResponseEntity readResponse = this.restTemplate.getForEntity("/time-entries/" + id, String.class); + + + assertThat(readResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + DocumentContext readJson = parse(readResponse.getBody()); + assertThat(readJson.read("$.id", Long.class)).isEqualTo(id); + assertThat(readJson.read("$.projectId", Long.class)).isEqualTo(123L); + assertThat(readJson.read("$.userId", Long.class)).isEqualTo(456L); + assertThat(readJson.read("$.date", String.class)).isEqualTo("2017-01-08"); + assertThat(readJson.read("$.hours", Long.class)).isEqualTo(8); + } + + @Test + public void testUpdate() throws Exception { + Long id = createTimeEntry(); + TimeEntry updatedTimeEntry = new TimeEntry(2, 3, LocalDate.parse("2017-01-09"), 9); + + + ResponseEntity updateResponse = restTemplate.exchange("/time-entries/" + id, HttpMethod.PUT, new HttpEntity<>(updatedTimeEntry, null), String.class); + + + assertThat(updateResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + + DocumentContext updateJson = parse(updateResponse.getBody()); + assertThat(updateJson.read("$.id", Long.class)).isEqualTo(id); + assertThat(updateJson.read("$.projectId", Long.class)).isEqualTo(2L); + assertThat(updateJson.read("$.userId", Long.class)).isEqualTo(3L); + assertThat(updateJson.read("$.date", String.class)).isEqualTo("2017-01-09"); + assertThat(updateJson.read("$.hours", Long.class)).isEqualTo(9); + } + + @Test + public void testDelete() throws Exception { + Long id = createTimeEntry(); + + + ResponseEntity deleteResponse = restTemplate.exchange("/time-entries/" + id, HttpMethod.DELETE, null, String.class); + + + assertThat(deleteResponse.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT); + + ResponseEntity deletedReadResponse = this.restTemplate.getForEntity("/time-entries/" + id, String.class); + assertThat(deletedReadResponse.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + + private Long createTimeEntry() { + HttpEntity entity = new HttpEntity<>(timeEntry); + + ResponseEntity response = restTemplate.exchange("/time-entries", HttpMethod.POST, entity, TimeEntry.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + + return response.getBody().getId(); + } +} From 1f0c992a9899e54b79ebf2a4ef2ca54dd84f8310 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 16:18:26 -0700 Subject: [PATCH 13/25] updated app name for zdd in pipeline.yml --- ci/pipeline.yml | 3 ++- manifest-production.yml | 3 +-- .../io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java | 4 ++++ src/main/java/io/pivotal/pal/tracker/TimeEntry.java | 4 ++++ src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java | 4 ++++ 5 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntry.java create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java diff --git a/ci/pipeline.yml b/ci/pipeline.yml index c34c3b2a9..0222db885 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -67,6 +67,7 @@ jobs: params: manifest: pal-tracker/manifest-review.yml path: pal-tracker-artifacts/pal-tracker-*.jar + current_app_name: pal-tracker environment_variables: WELCOME_MESSAGE: "Hello from the review environment" @@ -80,4 +81,4 @@ jobs: manifest: pal-tracker/manifest-production.yml path: pal-tracker-artifacts/pal-tracker-*.jar environment_variables: - WELCOME_MESSAGE: "Hello from the production environment" \ No newline at end of file + WELCOME_MESSAGE: "Hello from the production environment" diff --git a/manifest-production.yml b/manifest-production.yml index 21ab1e7e7..340b53421 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -2,5 +2,4 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - host: ps-pal-tracker - instances: 2 \ No newline at end of file + host: ps-pal-tracker \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java new file mode 100644 index 000000000..2f5a9f68a --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java @@ -0,0 +1,4 @@ +package io.pivotal.pal.tracker; + +public class InMemoryTimeEntryRepository { +} diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntry.java b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java new file mode 100644 index 000000000..3f945596c --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java @@ -0,0 +1,4 @@ +package io.pivotal.pal.tracker; + +public class TimeEntry { +} diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java new file mode 100644 index 000000000..0316a3fbd --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java @@ -0,0 +1,4 @@ +package io.pivotal.pal.tracker; + +public interface TimeEntryRepository { +} From ca1f963293baff131ca21f1ae5f5c1ff229e55c2 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Tue, 5 Dec 2017 16:42:43 -0700 Subject: [PATCH 14/25] Updated with TimeEntry Spring MVC changes --- build.gradle | 2 +- .../tracker/InMemoryTimeEntryRepository.class | Bin 0 -> 1945 bytes .../pal/tracker/PalTrackerApplication.class | Bin 734 -> 2276 bytes .../io/pivotal/pal/tracker/TimeEntry.class | Bin 0 -> 2901 bytes .../pal/tracker/TimeEntryController.class | Bin 0 -> 3445 bytes .../pal/tracker/TimeEntryRepository.class | Bin 0 -> 560 bytes .../InMemoryTimeEntryRepositoryTest.class | Bin 0 -> 3211 bytes .../pal/tracker/TimeEntryControllerTest.class | Bin 0 -> 5246 bytes .../pal/trackerapi/TimeEntryApiTest.class | Bin 0 -> 6651 bytes .../tracker/InMemoryTimeEntryRepository.java | 36 ++++++- .../pal/tracker/PalTrackerApplication.java | 21 ++++ .../io/pivotal/pal/tracker/TimeEntry.java | 102 ++++++++++++++++++ .../pal/tracker/TimeEntryController.java | 57 ++++++++++ .../pal/tracker/TimeEntryRepository.java | 8 ++ 14 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 out/production/classes/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntry.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryController.java diff --git a/build.gradle b/build.gradle index bc99220fb..41377c443 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter-web") testCompile("org.springframework.boot:spring-boot-starter-test") - + compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.1") // compile("com.fasterxml.jackson.core:jackson-databind") // compile("org.hibernate:hibernate-validator") // compile("org.springframework:spring-web") diff --git a/out/production/classes/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.class b/out/production/classes/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..2962951325efc5672b6ceab301eb3f4d0a8f8db7 GIT binary patch literal 1945 zcma)+Z&MRj6vm$oA#1`lprB$~75_E_G-|2UhEf$pi9uSSK^}EC_X6XBV zpLV8&PMzuax*w|3=j^UYBqF@X&An&u{hf23bMF23zrX(hFomZ%4C9)O>p7%wEsLZ0 zN@i0z4B%@SZshO{Zi?-e4By%)+nAQ=cR47`$n1OBb6aM2Y}~amYvZ1UfzWK|`(79r z-Lx=VedBC8rB-O%(nF{DdckRwEo7I>y61$gKwEgwyJI>wxsJD9s`}n~xoZ4U!)*GY zAW zj$3mABkRPN4qqF_JGGaqd2c~)_`z1&M`8oL0TiXSO0YB2ie(lRRJMcR};B1M+3Qw>k&X*N_5?Botg%u%Q--Gyx zR9N1~M|7(eSKsJ5>ss7{({9#Im$Mg|<`?S4+cj^Yzp2A{_Ds4^5k>pg<0U^W`;S^^o-jCBb z!xejr-eT4ZvSjbrMDY)#x6z+|`Rg_^vY6P0{TB}LFS|1F8~3Cj>^x5hF^|LKF!#SL%2MZT)k)uR?n|s8QO4zw~80b(XLok1dHm0dIP2m#0jA#XnlOeQQi3Wpj zp`OK|AKpQJH*mBo{lNqni&`Xb0+UH_&xw?%^-GxeL z`aiY)fKHwE59lA&>ASfI=k%gehs@-5_r3Sy*=L`3lfVD@<1YZKsHBm?tbyxk+(5>_ zTpCHtrZJC&6c%w)&z1~WDcr(sy;wG|Vqi6mbD`TEZCqm*xhowRtTQAsbGr=5ylM)D zv66H|rQ4|sug2>(S;kAM!R=k{NxkkHlfe_|Gc1*)vRrwn0&ZI_eFKj-o(j);#O+!* z&AF~E8$6K8$x@XLmkz^3=BHAdA9Bm)PRptWo^)E-9t!TYDDTCX*JKT3M--gEJK7Ph z@@1gBBf^lG8-$|vUw^_dxzlxYrd{bv+ELDN2)@Qgg>ROW*Rp(913K_{NBpe3r&dEb zfp`{J{KVDT5M1x)eA`#f!+KjZf--kq;W1c*r=dF50rvypJ?q$3n>ZmitH}dimrm1) z22hf8Rdv0F*piy=saWckG+PYovGD3j5w>oi2m7g7FAT);TP;{Q1` z17Di>3SToU4+twdWznIdh+*5nHzu;UYhc|(4jU%&*wn0@oP9`zfr5!G+&A$(iVU;w z;IFKjUCq_%V87tV6;|8#R&L+2wyBy17lxVG#sd>2Tw|DzJMA8wA%PL{s875uNw<-S zC^tl(jZy1d>`A>AQoessx}zdB$gFinlE}vadlCe$*5aY?NLjQy($sdSt>vhsHoDSo zYAGhQ3&VX<~2%c^hXBcWM&o}M|gRg`@W!#8?8L7>{os$ zl^E6r^v&CI6I&C$pxeoozx3L;SIMV-X81Mpeksp}FNT%D0_|M{)hO;Q!2{}Dx}YBK z6l;b3&0MWe-Op9`YsGS*TFaFmRT=IK*wD${5|@%Fx^NI?_iJiX`r*y~;~TedU(08; zb)(a3QaWKpW!}L<*qPb0sy$rS9Y7adeA#$C;TneMr&WKFAi^+C&x^FOXk;`F&%Xfs zohC#0h@K-o;}|Z{+zcHsfy=Z?;bTmaMF+2uw+r z{1d6Y#B=%;I>raIF!vI#aE|JHNQSXvoIfo$MZv%{J+Bb3tC*p*H}E@TJ-ZY{YxmrTywDo9;s zCiRfIaO_4hSS-_#O>jPV~N(y^(5 Np;c*{j=KM>3qMh>9|r&c diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntry.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntry.class new file mode 100644 index 0000000000000000000000000000000000000000..849679a180bf334880ca70698de428d4707f6ae7 GIT binary patch literal 2901 zcmai$U2_v<6vzMjmZV|ZhV+G(@RFd=G$}+BP^7IuX`!`1OG{}1m2JDWi=nCMwlfaH zh2xDMKrguRO1Z%qov~p?XB@r389#|1!V&-H*=>@>v`%NAm$TsJ) zWVjo{J(=&z@Ie$GYS6t!4ceH7meq3Up}XLZavxb=bIYDQJ_sA`b zmlmAj6^butu$D{f7}?$#o*XJI@?`tCx8mMde>Crwr=9sCB}rvq z#wmNU4~3?`?9tA#aj#TZ^)^brQ!K3V@ypJ_CvLegP0zzCe)%&xO=ZW%n(~Z_-$Q)6 z%j?*Z>t73?$w}QR7Fr)Xy|sBNIH`o^VpWe z%#MkYO{Ixi=51=!Z7sf)I#?8|!nmKVJ4I%yD|B0QR+b7k=cRBLdhfClCCRX=R{blw zmg1=tYG|GEnbzyhs>(haiD_7N)|P{kMau5ldeNuMFHQMnZ)HhC=T;_^(Ll;l#HX0b z1$V@g!nRe5bV~f&$l#cb6b{-rB*S4Gv2he>8wX_j5guqr|6jeMprLnh!Nw)zS&>>~ z0mS8Xuej)z8B^8!V{dW5#yl2eVD9X`bhWhyn25e~(A`|e`qI+i13jYZ%J8ugs;EO57c$`6CK! zP=RNvU>TKc0m~>v0n4Z}0+vyTl9i0yGnPn04*3wVz*$aOxu0M?=OReC#32WorMnL& z!^4BrmeNFWsck_71v}2XoKP0J1Fb4>M1hFoQxE_tT|J2TLjy=}Q;;`s`ad9f=CoGg z0OTY=PVE39On$KtiIZKdpJAg=u1>{^) z5WNxPd{dCOn}g_$AOi#$+yNwLat-e^1&K6*Txtq(zBx#w5oDMkBRhb|7eq3`?>t8- z&0oA0(%c)l{CBX-=lPdt%T*A~^Z6E*@jU+v|mx_g+x{kj^>hVis9`pf*FXex>n^jA!Y&LD`dy&`b1)-?%-KbK)RkC_y?Uz5LJ z&)f^x$<_+1$gnDCPbPx3F6+I)+K_cJSd+ga(NjTZvMUf+6?7+_P*B0X#*c-!^poARp2H7 literal 0 HcmV?d00001 diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class new file mode 100644 index 0000000000000000000000000000000000000000..eba45958f5a782b19307f4ca8e9093bb15de2462 GIT binary patch literal 3445 zcmcIm?^7F96g_W~k`k65LMd&jic}$l(5(su3!;=Ctu`bWQYzMp8?uFMLpI%P=rE4X z_|gAH865_l@uMI7n;ggUcDK8+w3DTyAM$qJ%e&{Ed+vSr?Vo@D`a6J0l#=MdNA2jw z#~oO}%>)+Pv4rI$;#iR@x8(G;96ph2pUSm6a>&YIRsNAnAfLdRK(}M=8Ov42HjR40 zs96G;tZ8XAbKi3GidN&#vGwi8hOHI-odEmmt46M|S2FCPUaD}VH*0O{l?~lC<=kJ4JG*8*3cW>(;aHW5VM}`3wxK)3Ty0Hn zb}VbzJ6gSFo7J6%w!UXPwd}{*uH)3Sf>Ezo)w-0*bY$G^&xAV=7;YIk4SGB#Pa zteZPk-D%k5_vF9iH`kibJSkl)G#JjFv0>It3cXORGU<-VI0dF&sKryGq?OESS<{2P z+L~2&R`ptqq7ifW- z>CW!6gCzy6m~}^BJarlY_2|3jJeeDgS<$j|6-gCYnr)3***&)pPNbl9ZJ#EX9?wzp zvHbr3bQwtjf#`Ly)@Z7Wwz5&-X%m=^ToFsIMRsG_wHdaj8i7y1*D(tFP<=GRM ziR^*B(+SO)+|!u4$3JA(smX>lVR$!|iar!nyoTNc)>T|WQD8J;4^QB(iVfV87@w=y z#1|^=%juWi+5;8W@Rf@9v87@LU#rk@Paxgub_^qBmkHZczVyi0=207JWiJp*$=k7| zqwpFQSc!Cdv~tbVTzRQ_w|Nb+Lplv!4vEFW@zYe}VE7X9~`9 z>_UuVH@^sb&`#?C&dz(gaRC=Oqn(Q?h;|8s+$k|P`IMFe>7SvDA0wt<^*6*fGlw{H zgti~~m**hqg7u?=&=P#eGYh~6a2c-?x&tcS;G2RsUE3jsSV(Uqpf`e1q+EJ~ zE>Q(zv^xv&7X}kZ`((#mvR&Ml;cwzTL>*T{(wa;98s4RIDc(Gt3qH?DW|H_ZIs^{k zvgNv`oJ=AZsHCNO`+ZuTLhl6%Ww=AICr$dI{J^ISZ`2hfa||_x+=Q>ngzx9&s*KZJ zhV?MPh&9)J+Jg&J8TD163tttL3QkeWSg6nCP@n68KGP)HtWT%H1y3I@wKG9#R|zJ| zw%<=Jw|bnr{7g0%*c7XD+U4q1Z-2nG-{0Zv~IR7x% GKK~Cc3a-%r literal 0 HcmV?d00001 diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..7f2d9dfdfd81f3a85f7ae54dbc34aea50b2ce830 GIT binary patch literal 560 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 c@;XT4Wq7%cS2I;#McEkFUx6>&PMXaE2J literal 0 HcmV?d00001 diff --git a/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java index 2f5a9f68a..74a934497 100644 --- a/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java +++ b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java @@ -1,4 +1,38 @@ package io.pivotal.pal.tracker; -public class InMemoryTimeEntryRepository { +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class InMemoryTimeEntryRepository implements TimeEntryRepository{ + private HashMap timeEntries = new HashMap<>(); + + @Override + public TimeEntry create(TimeEntry timeEntry) { + timeEntry.setId(timeEntries.size() + 1); + timeEntries.put(timeEntry.getId(), timeEntry); + return timeEntry; + } + + @Override + public TimeEntry find(Long id) { + return timeEntries.get(id); + } + + @Override + public List list() { + return new ArrayList<>(timeEntries.values()); + } + + @Override + public TimeEntry update(Long id, TimeEntry timeEntry) { + timeEntries.replace(id, timeEntry); + timeEntry.setId(id); + return timeEntry; + } + + @Override + public void delete(Long id) { + timeEntries.remove(id); + } } diff --git a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java index 264628037..4009fc880 100644 --- a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -1,8 +1,14 @@ package io.pivotal.pal.tracker; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; @SpringBootApplication @@ -13,4 +19,19 @@ public static void main(String[] args){ } + @Bean + TimeEntryRepository timeEntryRepository(){ + + return new InMemoryTimeEntryRepository(); + } + + @Bean + public ObjectMapper jsonObjectMapper() { + return Jackson2ObjectMapperBuilder.json() + .serializationInclusion(JsonInclude.Include.NON_NULL) // Don’t include null values + .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) //ISODate + .modules(new JavaTimeModule()) + .build(); + } + } diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntry.java b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java index 3f945596c..16e99afa3 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntry.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java @@ -1,4 +1,106 @@ package io.pivotal.pal.tracker; +import java.time.LocalDate; + public class TimeEntry { + + private long id; + private long projectId; + private long userId; + private LocalDate date; + private int hours; + + + public TimeEntry() { + } + + public TimeEntry(long projectId, long userId, LocalDate date, int hours) { + this.projectId = projectId; + this.userId = userId; + this.date = date; + this.hours = hours; + } + + public TimeEntry(long id, long projectId, long userId, LocalDate date, int hours) { + this.id = id; + this.projectId = projectId; + this.userId = userId; + this.date = date; + this.hours = hours; + } + + public int getHours() { + return hours; + } + + public void setHours(int hours) { + this.hours = hours; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public long getProjectId() { + return projectId; + } + + public void setProjectId(long projectId) { + this.projectId = projectId; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TimeEntry timeEntry = (TimeEntry) o; + + if (id != timeEntry.id) return false; + if (projectId != timeEntry.projectId) return false; + if (userId != timeEntry.userId) return false; + if (hours != timeEntry.hours) return false; + return date != null ? date.equals(timeEntry.date) : timeEntry.date == null; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (int) (projectId ^ (projectId >>> 32)); + result = 31 * result + (int) (userId ^ (userId >>> 32)); + result = 31 * result + (date != null ? date.hashCode() : 0); + result = 31 * result + hours; + return result; + } + + @Override + public String toString() { + return "TimeEntry{" + + "id=" + id + + ", projectId=" + projectId + + ", userId=" + userId + + ", date='" + date + '\'' + + ", hours=" + hours + + '}'; + } + } diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java new file mode 100644 index 000000000..1f460951a --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -0,0 +1,57 @@ +package io.pivotal.pal.tracker; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/time-entries") +public class TimeEntryController { + + private TimeEntryRepository timeEntriesRepo; + + public TimeEntryController(TimeEntryRepository timeEntriesRepo) { + this.timeEntriesRepo = timeEntriesRepo; + } + + @PostMapping + public ResponseEntity create(@RequestBody TimeEntry timeEntry) { + TimeEntry createdTimeEntry = timeEntriesRepo.create(timeEntry); + + return new ResponseEntity<>(createdTimeEntry, HttpStatus.CREATED); + } + + @GetMapping("{id}") + public ResponseEntity read(@PathVariable Long id) { + TimeEntry timeEntry = timeEntriesRepo.find(id); + if (timeEntry != null) { + return new ResponseEntity<>(timeEntry, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @GetMapping + public ResponseEntity> list() { + return new ResponseEntity<>(timeEntriesRepo.list(), HttpStatus.OK); + } + + @PutMapping("{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody TimeEntry timeEntry) { + TimeEntry updatedTimeEntry = timeEntriesRepo.update(id, timeEntry); + if (updatedTimeEntry != null) { + return new ResponseEntity<>(updatedTimeEntry, HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + + @DeleteMapping("{id}") + public ResponseEntity delete(@PathVariable Long id) { + timeEntriesRepo.delete(id); + + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java index 0316a3fbd..dc53a1555 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java @@ -1,4 +1,12 @@ package io.pivotal.pal.tracker; +import java.util.List; + public interface TimeEntryRepository { + + TimeEntry create(TimeEntry timeEntry); + TimeEntry find(Long id); + List list(); + TimeEntry update(Long id, TimeEntry timeEntry); + void delete(Long id); } From 367f1e44494328a5d6e539df35bf3c11bc42d452 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 11:45:04 -0600 Subject: [PATCH 15/25] Add tests for JDBC lab --- .../tracker/JdbcTimeEntryRepositoryTest.java | 159 ++++++++++++++++++ .../pal/trackerapi/TimeEntryApiTest.java | 12 ++ 2 files changed, 171 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java diff --git a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java new file mode 100644 index 000000000..e1eac20fc --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java @@ -0,0 +1,159 @@ +package test.pivotal.pal.tracker; + + +import com.mysql.cj.jdbc.MysqlDataSource; +import io.pivotal.pal.tracker.JdbcTimeEntryRepository; +import io.pivotal.pal.tracker.TimeEntry; +import io.pivotal.pal.tracker.TimeEntryRepository; +import org.junit.Before; +import org.junit.Test; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.sql.Date; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JdbcTimeEntryRepositoryTest { + private TimeEntryRepository subject; + private JdbcTemplate jdbcTemplate; + + @Before + public void setUp() throws Exception { + MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + + subject = new JdbcTimeEntryRepository(dataSource); + + jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("DELETE FROM time_entries"); + + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + + @Test + public void createInsertsATimeEntryRecord() throws Exception { + TimeEntry newTimeEntry = new TimeEntry(123, 321, LocalDate.parse("2017-01-09"), 8); + TimeEntry entry = subject.create(newTimeEntry); + + Map foundEntry = jdbcTemplate.queryForMap("Select * from time_entries where id = ?", entry.getId()); + + assertThat(foundEntry.get("id")).isEqualTo(entry.getId()); + assertThat(foundEntry.get("project_id")).isEqualTo(123L); + assertThat(foundEntry.get("user_id")).isEqualTo(321L); + assertThat(((Date)foundEntry.get("date")).toLocalDate()).isEqualTo(LocalDate.parse("2017-01-09")); + assertThat(foundEntry.get("hours")).isEqualTo(8); + } + + @Test + public void createReturnsTheCreatedTimeEntry() throws Exception { + TimeEntry newTimeEntry = new TimeEntry(123, 321, LocalDate.parse("2017-01-09"), 8); + TimeEntry entry = subject.create(newTimeEntry); + + assertThat(entry.getId()).isNotNull(); + assertThat(entry.getProjectId()).isEqualTo(123); + assertThat(entry.getUserId()).isEqualTo(321); + assertThat(entry.getDate()).isEqualTo(LocalDate.parse("2017-01-09")); + assertThat(entry.getHours()).isEqualTo(8); + } + + @Test + public void findFindsATimeEntry() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (999, 123, 321, '2017-01-09', 8)" + ); + + TimeEntry timeEntry = subject.find(999L); + + assertThat(timeEntry.getId()).isEqualTo(999L); + assertThat(timeEntry.getProjectId()).isEqualTo(123L); + assertThat(timeEntry.getUserId()).isEqualTo(321L); + assertThat(timeEntry.getDate()).isEqualTo(LocalDate.parse("2017-01-09")); + assertThat(timeEntry.getHours()).isEqualTo(8); + } + + @Test + public void findReturnsNullWhenNotFound() throws Exception { + TimeEntry timeEntry = subject.find(999L); + + assertThat(timeEntry).isNull(); + } + + @Test + public void listFindsAllTimeEntries() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (999, 123, 321, '2017-01-09', 8), (888, 456, 678, '2017-01-08', 9)" + ); + + List timeEntries = subject.list(); + assertThat(timeEntries.size()).isEqualTo(2); + + TimeEntry timeEntry = timeEntries.get(0); + assertThat(timeEntry.getId()).isEqualTo(888L); + assertThat(timeEntry.getProjectId()).isEqualTo(456L); + assertThat(timeEntry.getUserId()).isEqualTo(678L); + assertThat(timeEntry.getDate()).isEqualTo(LocalDate.parse("2017-01-08")); + assertThat(timeEntry.getHours()).isEqualTo(9); + + timeEntry = timeEntries.get(1); + assertThat(timeEntry.getId()).isEqualTo(999L); + assertThat(timeEntry.getProjectId()).isEqualTo(123L); + assertThat(timeEntry.getUserId()).isEqualTo(321L); + assertThat(timeEntry.getDate()).isEqualTo(LocalDate.parse("2017-01-09")); + assertThat(timeEntry.getHours()).isEqualTo(8); + } + + @Test + public void updateReturnsTheUpdatedRecord() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (1000, 123, 321, '2017-01-09', 8)"); + + TimeEntry timeEntryUpdates = new TimeEntry(456, 987, LocalDate.parse("2017-01-10"), 10); + + TimeEntry updatedTimeEntry = subject.update(1000L, timeEntryUpdates); + + assertThat(updatedTimeEntry.getId()).isEqualTo(1000L); + assertThat(updatedTimeEntry.getProjectId()).isEqualTo(456L); + assertThat(updatedTimeEntry.getUserId()).isEqualTo(987L); + assertThat(updatedTimeEntry.getDate()).isEqualTo(LocalDate.parse("2017-01-10")); + assertThat(updatedTimeEntry.getHours()).isEqualTo(10); + } + + @Test + public void updateUpdatesTheRecord() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (1000, 123, 321, '2017-01-09', 8)"); + + TimeEntry updatedTimeEntry = new TimeEntry(456, 322, LocalDate.parse("2017-01-10"), 10); + + TimeEntry timeEntry = subject.update(1000L, updatedTimeEntry); + + Map foundEntry = jdbcTemplate.queryForMap("Select * from time_entries where id = ?", timeEntry.getId()); + + assertThat(foundEntry.get("id")).isEqualTo(timeEntry.getId()); + assertThat(foundEntry.get("project_id")).isEqualTo(456L); + assertThat(foundEntry.get("user_id")).isEqualTo(322L); + assertThat(((Date)foundEntry.get("date")).toLocalDate()).isEqualTo(LocalDate.parse("2017-01-10")); + assertThat(foundEntry.get("hours")).isEqualTo(10); + } + + @Test + public void deleteRemovesTheRecord() throws Exception { + jdbcTemplate.execute( + "INSERT INTO time_entries (id, project_id, user_id, date, hours) " + + "VALUES (999, 123, 321, '2017-01-09', 8)" + ); + + subject.delete(999L); + + Map foundEntry = jdbcTemplate.queryForMap("Select count(*) count from time_entries where id = ?", 999); + assertThat(foundEntry.get("count")).isEqualTo(0L); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index b742e5447..2b7464304 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -1,8 +1,10 @@ package test.pivotal.pal.trackerapi; import com.jayway.jsonpath.DocumentContext; +import com.mysql.cj.jdbc.MysqlDataSource; import io.pivotal.pal.tracker.PalTrackerApplication; import io.pivotal.pal.tracker.TimeEntry; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +14,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.time.LocalDate; @@ -30,6 +33,15 @@ public class TimeEntryApiTest { private TimeEntry timeEntry = new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8); + @Before + public void setUp() throws Exception { + MysqlDataSource dataSource = new MysqlDataSource(); + dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("TRUNCATE time_entries"); + } + @Test public void testCreate() throws Exception { ResponseEntity createResponse = restTemplate.postForEntity("/time-entries", timeEntry, String.class); From 691c721599fa9b6edc86de2e3d56720125db3a71 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 12:16:03 -0700 Subject: [PATCH 16/25] Updated JDBC template and MSQL DB service --- build.gradle | 35 ++++++- ci/build.yml | 16 +++- ci/pipeline.yml | 1 + databases/tracker/create_databases.sql | 10 ++ .../tracker/migrations/V1__initial_schema.sql | 11 +++ manifest-review.yml | 4 +- .../pal/tracker/JdbcTimeEntryRepository.class | Bin 0 -> 5866 bytes .../pal/tracker/PalTrackerApplication.class | Bin 2276 -> 2380 bytes .../tracker/JdbcTimeEntryRepositoryTest.class | Bin 0 -> 6436 bytes .../pal/trackerapi/TimeEntryApiTest.class | Bin 6651 -> 7269 bytes .../pal/tracker/JdbcTimeEntryRepository.java | 90 ++++++++++++++++++ .../pal/tracker/PalTrackerApplication.java | 7 +- .../tracker/JdbcTimeEntryRepositoryTest.java | 3 +- 13 files changed, 166 insertions(+), 11 deletions(-) create mode 100644 databases/tracker/create_databases.sql create mode 100644 databases/tracker/migrations/V1__initial_schema.sql create mode 100644 out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class create mode 100644 out/test/classes/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.class create mode 100644 src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java diff --git a/build.gradle b/build.gradle index 41377c443..9a3205ece 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,10 @@ +import org.flywaydb.gradle.task.FlywayMigrateTask + plugins { id "java" id "org.springframework.boot" version "1.5.4.RELEASE" + id "org.flywaydb.flyway" version "4.2.0" } - repositories { mavenCentral() } @@ -11,19 +13,44 @@ dependencies { compile("org.springframework.boot:spring-boot-starter-web") testCompile("org.springframework.boot:spring-boot-starter-test") compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.1") + compile("org.springframework.boot:spring-boot-starter-jdbc") + compile("mysql:mysql-connector-java:6.0.6") // compile("com.fasterxml.jackson.core:jackson-databind") // compile("org.hibernate:hibernate-validator") // compile("org.springframework:spring-web") // compile("org.springframework:spring-webmvc") // compile("org.springframework.boot:spring-boot-starter") // compile("org.springframework.boot:spring-boot-starter-tomcat") - +// testCompile("org.springframework.boot:spring-boot-starter-test") } +def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" bootRun.environment([ - "WELCOME_MESSAGE": "World", + "WELCOME_MESSAGE": "hello", + "SPRING_DATASOURCE_URL": developmentDbUrl, ]) +def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ "WELCOME_MESSAGE": "Hello from test", -]) \ No newline at end of file + "SPRING_DATASOURCE_URL": testDbUrl, +]) + +flyway { + url = developmentDbUrl + user = "tracker" + password = "" + locations = ["filesystem:databases/tracker/migrations"] +} + +task testMigrate(type: FlywayMigrateTask) { + url = testDbUrl +} + +//bootRun.environment([ +// "WELCOME_MESSAGE": "World", +//]) +// +//test.environment([ +// "WELCOME_MESSAGE": "Hello from test", +//]) diff --git a/ci/build.yml b/ci/build.yml index 663282c49..ea12986b1 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -10,7 +10,6 @@ inputs: - name: pal-tracker - name: version - outputs: - name: build-output @@ -19,7 +18,18 @@ run: args: - -exc - | + + export DEBIAN_FRONTEND="noninteractive" + + apt-get update + + apt-get -y install mysql-server + service mysql start + cd pal-tracker + mysql -uroot < databases/create_databases.sql chmod +x gradlew - ./gradlew -P version=$(cat ../version/number) build - cp build/libs/pal-tracker-*.jar ../build-output + ./gradlew testMigrate clean build || (service mysql stop && exit 1) + service mysql stop + + cp build/libs/pal-tracker-*.jar ../build-output \ No newline at end of file diff --git a/ci/pipeline.yml b/ci/pipeline.yml index 0222db885..26799f63c 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -79,6 +79,7 @@ jobs: - put: production-deployment params: manifest: pal-tracker/manifest-production.yml + current_app_name: pal-tracker path: pal-tracker-artifacts/pal-tracker-*.jar environment_variables: WELCOME_MESSAGE: "Hello from the production environment" diff --git a/databases/tracker/create_databases.sql b/databases/tracker/create_databases.sql new file mode 100644 index 000000000..a0674bdda --- /dev/null +++ b/databases/tracker/create_databases.sql @@ -0,0 +1,10 @@ +DROP DATABASE IF EXISTS tracker_dev; +DROP DATABASE IF EXISTS tracker_test; + +CREATE DATABASE tracker_dev; +CREATE DATABASE tracker_test; + +CREATE USER IF NOT EXISTS 'tracker'@'localhost' + IDENTIFIED BY ''; +GRANT ALL PRIVILEGES ON tracker_dev.* TO 'tracker' @'localhost'; +GRANT ALL PRIVILEGES ON tracker_test.* TO 'tracker' @'localhost'; diff --git a/databases/tracker/migrations/V1__initial_schema.sql b/databases/tracker/migrations/V1__initial_schema.sql new file mode 100644 index 000000000..3e605c0b5 --- /dev/null +++ b/databases/tracker/migrations/V1__initial_schema.sql @@ -0,0 +1,11 @@ +CREATE TABLE time_entries ( + id BIGINT(20) NOT NULL AUTO_INCREMENT, + project_id BIGINT(20), + user_id BIGINT(20), + date DATE, + hours INT, + + PRIMARY KEY (id) +) + ENGINE = innodb + DEFAULT CHARSET = utf8; diff --git a/manifest-review.yml b/manifest-review.yml index cd3361d30..897eba97d 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -2,4 +2,6 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - host: ps-pal-tracker-review \ No newline at end of file + host: ps-pal-tracker-review + services: + - tracker-database \ No newline at end of file diff --git a/out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class b/out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..b083fab63ebde879f572f8a321bd229717739b95 GIT binary patch literal 5866 zcmbtY2Y3_b8Gg?eK3P7%kuk1`Lj)L*Wfd_}3Na89BZ7^LiDgWjMbFYC*IYBcjG&Kf+{9r+ozhO|#$-&;#8SGU#Rfdo zJrWwG)O=nuBy`o_8`e(@TBKxZOlBr>s#!FsZ*abR+Y%W)md_m5O*I?K^WQYo)TCy_ zMlw@cJZBoGdUgoe+DY-k)M?YkTC%l5F>59@GhX7MfYJY%X`YrfY|G>_<_-z%;lxq( zxOy^HIF^m=QcX3f7mbwG(={r=*Qck6%Hl*Or|m0Fjcdk;I-cc8FrlZ^?5JvF#NIjd znMX1O3G1stV+E@O&#;!)3mF1)ie3iNu3iaCE8D~9rVP!>irwLAD$3}0)h1zh%#t}0 zR?deZIHBb{Ykar2zR`6fv*&&V*|qjQX{jn=%}X-I2X zO=xJDqd_E~l2xb1(`si6pw!osA<;$=gy5}AFF z85uvB((-0T&lP0cC*%Ivwrr5FwW2Nk^Yhx!Pq3aONd2rX$45K6JJ(A%FpvB$d(x-p za#~85N+d)nTa~=u(DJIGrRkcfO=&ri2Z5AJDxtMZ>yf~kD3mxPbd+f-%ClR=?u=*& z9aYMEoiK@s>00Jmq+mNf$Yj1&*UbXkWPVUHkLc;b;s$)E8K?PirWvY&X`EHC8Bc7+ zxy3SOnsI}I^SGekYS5|3{BH=MaEHQM^a&C+Rb?yH>~(WWy%`^-Q4c715Fb&H#6vPZ zs^DYzxQtII_#_^d@hJtL#%B~HFeqUl8BfIfMnai%G?X{=qm0rau`U)g!&->qiiVD` z;TA%>hld73q8}Y%u#AjW2p!xLAC8kbv^{hUVX^?Uy2a4A5T)P|d{#oJ)Nf_$imy8dOg!=c549)gFT$bs&9SwPc+7%kznK%$nhQh?f z`RkJLV+B9KPi6c}!O!sv1;50v6#N>$k+7~xFssxSv%d<1dl8oJS(bdc2Cq-J4NgLL zrCLa8kCt3asp-HEb*0J>F4mlgCI;N4;cE7O?zLLYv+D z=k!(W-g4F3^c;Go$%JN-ofmd# za!^VI&d93M+rGKob z8B{vy1k%!(WH07yc9VS*dULHh0@ha(cRnfSh*ziG!|BUl1#PuNyWd_MiQZvPA$I%i z%ch^BVk5T)c;AS%%r#4b2(^`P6&DVtPY(0q1MudN0{_^d!%#c zCkY!W$S(mbhTJ*TOQe^%!somt9tF*`@mLV<9}u>P6FU=H!u=QdE#5cqz2z$zXuOO+ zDflz~uHZ>xbUP?!wKSMxpZB=+*TRJ_F! z@(y7k@1oFBd5{WeI5!B zoyNk0k?0H-NgU`FpF{IBF1wGkb?D`Dk;Qj8{8&zXQA=aUmgQ;;V-NbN$&U~Qu$P>Y z7||WwgJ`fkZWlU(LEi&t8LRJ3a@R18B@dystgL}7zy^<7?XHq`?4$F-t^K&30tJ{` zXo%ogWQktNr*9f<7t7mf$y01fN1h$5VV)&1!l#gSH-B~$AoW_(9_P0VB|PoKBsu2e zYMHfKz}4mF(2*Q#;OdHVSe_h{yOYzn@?fNU7I@1k5e)r8TF^$#D;T0~hG#R+u4ZsV z_-rtHT|avrKWT>rs{r!=MjadXI$*X~Hik-I3f<*w30Qb1n2CcPC|g_=Ew+m0I?+KK zC~u+bB9A({XRt!TJy;^p?DoxIWjz?Ad*PD=u=Q3}*nzsiBfZ0w-hnaPNONx@wT!xD zy)tgbE!>DSxD|(*17P=Z)ZUIJOYNo$XdR0LSIwZa4w2w$=W~toxpoHOI;Lk=Bp8`N zcOA|mS`M=f^ANS|$6_W&8-{Tu0~=vzH!^7i%u8)GmTla!g2$1fmBKI|KX0X3Y^9D_ zckxW5LQi+_@q}PqJqDw`2vcDOF(0A}xLjs08@Lq7FoX5=h|1huF@p_Umd#s6&q0F% zEX`J|X2jQE9oDi{ba^tM$BH%y**p$of+hu62}h8jNv@GJ>L@AdFp0nMGeD}p;2-!a c+L7a4(YCtHp&?;AMEAL1M!4QW^sy#Zryz{R3uJ z?EDEPEJ!rbJ@GHFb8F(#mGQkwhzrSN&ikI3bIyExi+#ONIsEnUGmvM=!zH8X_2?t3 z$$3P%;c=6G7oS^UcUv>yGDtoocQki3_dL##3HkjHhTc^UFO_=dHiFl|8-M5Jrau`p zgD3S@jcTo6uc~CRytZ0hUVl-W+G;j-mulPfo%LqDv0G4hYbR1g;=d95B{itrVMBoj zihkRn+LuoWG{h)sMhv5jX&xG;m^RE%Qk)O3=WhmE&-{hejas#-nKhJoWSHZzB5l94 zrfr)ubatKvLxq$gYkxR#``meZIeE(S`kvbF&R%p;kr#hr|9$jlM8)i$9&L%EN!TOZ zSt;TWkq>u25nah0(DFe@gt%ZUO7IF71rvWoQrm@zoDMo=|C;onu1l(-o0J_tQ(ozk swQkx-(IX>6nye_o9P=kLcbxOD+Yx1Wa{46my6{Pvo_Ku5nHWp{2G*`;6#xJL delta 416 zcmZut%}N4M7(I7p?9L1mm6_s3v#d1DF|$AW(SEEnO^G%xrx-#>4U@oUh!$~)7Ck|` zz(r8d14xh2Gen!BI~mcU#r^I%=brO@-0#KyI#BFR3;#SBGUMQ_(OYl(AL#f}sS1as_CaIpe$wl7GODDxT?ut*TI z>qV>!4gU)3s)!wQru*&&o|MQ6w}hVVPo8!y48#Y+#e1zhayl`iW6H)(eLf z^QcLY*06}trt$F9X&mXmIzJVBi4qO<)Wy$hs|z0su24hjcL$li1e z0to-O`~<1=0NXvs0vx>deCuuuajbPcaKgoz=jYYSZ-QYy>rT#=JUrpWAm@Jg`0hsd f8a#i}p?}gMcUkjBe|!2lPw}Jj&S9(|nR0&t6!boa diff --git a/out/test/classes/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.class b/out/test/classes/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.class new file mode 100644 index 0000000000000000000000000000000000000000..5099ef34fd7d283cd0b5f793baabc7e696d53ab2 GIT binary patch literal 6436 zcmcgw33yyp75?8O?Mx;QNYkVXU3jHPC+VbpHR8qj|Y(T;fNDd$uSYfgzz%y!-HaSv>czrL*nspPU(O% zP@ZrGo>bf#mV#i|NQP3z zSkj8dLn;2Z%xLT)-3&#HQN1l;nd3crDw#H{q&eQKKph#_8;j_psd&`V#p;}5Ek?qywkjwI)buMTZA}i)$Z26Cp?7CS_v&UO zx;IWqWjGm&#`~kDA?A*-)EYL@3O0rYG z^t3V@OOA#{$J6`bq1cEWd6$@-msjFaf7^kWp0bQ&BJIWJ6fEw^BrH1WH_`?JY)K>t zpL{K2CnO^oCK}qN4-y3$SQayN0@jg8>!y`%abZiC1_*0H-=7yMICIub5&7t1cNpAY zGLsmPTP3g@iH=1>8Ow-=x}qtnEhxrvJe9+9L$T@>dm4`>hC;oT*wAK|WQU|n^Hv(H zkb7I4>GkMV#!RFm!+NVscfgg>{6Qlzu$}*1*()d$C_14p5gLw(?o2$sYgkWo zC#~%wI|U2lM%uF1$KyG>hE771=WNJXg)6HtmoVV<$BDMgj;_daStdoM=!K;E8b+ciszZk)uLra?2MC=y6?`A5D(w0<+u;zs=322QULTdiUj zUa4TktTa|o-Mh1=qq}|2c`cEa-W`2Ct!;bydcs~jq2lv+QpFeWMFnf7X$RB9A>4mS zMK2->YEQlwsOSS5Ky{?2ue-G+(xwS4_Xt9B6IFa!L{ichY4zeOD!z)ZsrWj+K{Tcb zF2@H^4QuNgHr3TP)YUh6@l6%q!nZ|?-|^zRD!zyBD_GS_;A56ns|}jT(IU^<{$btJ zHDf?KM?06>DQihqgw1~yPvL1VexTxq_>qbq<0lF{#()<;Rq+gd=Ecud`~t_kcvi(P z@he7?GLxdu?lDMVZ-&(`N2P*T3Qm#sMeAX=(N+8!zY*(y>&5R_-otEBY&X9b&i)|e z&w24j6@S8?Rs01{3lGn!_^TNGO)UI7{vouU_u`-8-SY~DI=Xw?dLmj!cVtI#o&jSZ zs1;%hYEEcDO+*&dr0*K7za`w))~f}Ynwo-I!`gL0ZQa_2pth=j@v5NKSfk=!__vA| z1QD6{xMzVWbYkM zTly%E>mfF%#F&D!1FoaMIUH)Hinv0Yv1Xi=eWDet_{FyTK*(Xea}YO+>ws53pvN+z zFe+t)@(>fHeL*roVL78I%mOEJ;A-?aO*V2rgE~(ijAr7z?<{oI6&aWAZCEV&3&|r> z)FjI=6*bfR47vTa4oA&ieP2dT#Pntdh~kBGyCl%rX$N%Ns}7ND?QE^AaP1>r`-Rff3 z2CrmpY#e%A=%R;nd2sBw?k08v%n29`qdJal#cN%?m{FV|4xDIO+^I(F`(`v{gj%FP z+|{`C_S?vCly=W`hncr~VjFwY;vQ_-TDC$Ib#m+WVnao|46>m8VUhVgmCD zE|>F%3gC43umtn4jDKgKhIy>TN^HhiGL!|jJ0}z$)?p7`%aEd6S<277h;bx>AK<3~ zJr}C7f_6(OyXPn>cUNJ46{;&2PGC`m?+6x$8CKcqNu2IMmyB>p=|fn$yCj&!vI#8j z&Ekyix=L*lD?FIMnT|M%mAmRH&&o+@l)AR?Wd&h%UU;??K8jUY1SU`;-Dgot8+2`3 zuJBEO)>rNlu#`aT#Mvn0Gk|J>pA@ zYj$w?^mpddZ*$V0F2!U%#ly5AC>@nN*hr=dZd73tw{a3p9)wp*QZS)SA_dM|>2c;t zkF%omIG;YfZQSQ}EJg=^DeJ@ryd13vvvj*i@NO6RqfUB@aXwOVcc)8gUp}dblhmRq zxsc6SY%UbxsS?5-RIp_% zYQ2QizLelyMqn=In=4#U?02A8&iDHzxkCi>fP`YY#K&`qE9;rK=<(U9iFeJExKw{> zEx#q--CZreD=$30<>vr%w|xJUmS4*=8n}|U9VBj75x1+^8m{H>dmT(%Pw5S~8#nSd z@tY)ecH7V9vAabg##}3`p-P@vO1>IwNqmNC KxP-Pnxb(l&e}%dL literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class b/out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class index 0c3e8de7c31ebec4e0bc347205a1a50a59e888e7..07dc1c0e6bc4a34df5c251213d77def770420121 100644 GIT binary patch literal 7269 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 Tyd7_)Opx<$#xL-eZPot+`t9!2 delta 2688 zcma)-d0bRg6vuyW7T%l3bDMAhH&P-UW)Klm5Ct?`638}jTv7uP%me|I)Oy)sWo64t zElVx4GTRhvKykseGAqqiD_g9z(pKBktlynCGl-0Re1F_`?>%=r-}AfY9_oL*kG}il zrmX-{B!`NYSRmszJDcL08>mRT6x^=j4lGn*!JQ5a!(F1dTfrh3_sF{My-loSfHRz{5OhXlPKzg z%UOaItAfp04s5|z8QWA0K!ez0d(i9GTR2yKCZXu~z8~BU`Sr=HX(aK~8S_riMM(t050>X~;pYhT#}t ztW^5<*{9)cyd&dX4e#N786Rlaj}K*hq~T*6knxFzPw|S}tD=(c|R8TphP)uZ(5|=o$qNsdCp@fLI#KOsyMPklT8Q)2e z;}VNYXO>rJn1ON)-{S`jKjMI~($TdIe!|aU?&B`p_yT!H3p$Xj2WR59j2w71e+ib!eOGcD8f2=co=F5p6>Q z+Seh*;X}uKw^Q%b%n}8w51nc262@dVOzu};~Cb2+J7m`UBmr1d#kaymt)cll8n!F2T7hUNU{|mndfo#aP~Zf zWY08Pv~43?66%m(^TD0(aeA5(NV7-V$(!LwY{noNx*dJd7X67MjUJ~X3j-PH3=|*} z*Rmz_FvvV>fPoz)m_$|*=Xr4zX;?TjlQX;`77sIH^7T6qrJm0r7dxEIyC( zOycvLjrd%=Z9q1u45$1?>PTVg%iz@F(X=%IeH!Pf0nR$4*-&^I;HV#TJuKcR^o>G4XKEdk){a^@a)my_(62^X zJqCE)QZ3Shkr>0^$`?ZI%f1fHG+cmkIicn@d}`RM0PA3lol1kqbC2TrwEc zBpK+GZX}%_O$7N&fn)i&PGG{DNa0SxT$W2I@fwPG4`qFX!WJ<}F@c3*aySr^80^C} zjHQLEtj091XypnEd6B$~VlXSsbEJ5TM{w! zzd>f^wD^!+hoS2@-+Ta^exkFOwX1Z*Vh%Mh&+n0$?9&4tX-J}{l{`W$I$@^Yqo5zA zZC1!On;uzF#Vstj&VSdsur`PGxdmMOdNyw0(@?;G-&llOcw=woSQQQl^YJ=v%8dRG D2^sW; diff --git a/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java b/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java new file mode 100644 index 000000000..73126b35a --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/JdbcTimeEntryRepository.java @@ -0,0 +1,90 @@ +package io.pivotal.pal.tracker; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; + +import javax.sql.DataSource; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.util.List; + +import static java.sql.Statement.RETURN_GENERATED_KEYS; + +public class JdbcTimeEntryRepository implements TimeEntryRepository{ + + private final JdbcTemplate jdbcTemplate; + + public JdbcTimeEntryRepository(DataSource dataSource){ + this.jdbcTemplate = new JdbcTemplate(dataSource); + + } + + @Override + public TimeEntry create(TimeEntry timeEntry) { + KeyHolder generatedKeyHolder = new GeneratedKeyHolder(); + //id = genKeyHolder.getKey(); + //id = timeEntry.getId(); + jdbcTemplate.update(connection -> { + PreparedStatement statement = connection.prepareStatement( + "INSERT INTO time_entries (project_id, user_id, date, hours) " + + "VALUES (?, ?, ?, ?)", + RETURN_GENERATED_KEYS + ); + statement.setLong(1, timeEntry.getProjectId()); + statement.setLong(2, timeEntry.getUserId()); + statement.setDate(3, Date.valueOf(timeEntry.getDate())); + statement.setInt(4, timeEntry.getHours()); + + return statement; + }, generatedKeyHolder); + + return find(generatedKeyHolder.getKey().longValue()); + } + + @Override + public TimeEntry find(Long id) { + return jdbcTemplate.query( + "SELECT id, project_id, user_id, date, hours FROM time_entries WHERE id = ?", + new Object[]{id}, + extractor); + } + + @Override + public List list() { + return jdbcTemplate.query("SELECT id, project_id, user_id, date, hours FROM time_entries", mapper); + } + + @Override + public TimeEntry update(Long id, TimeEntry timeEntry) { + jdbcTemplate.update("UPDATE time_entries " + + "SET project_id = ?, user_id = ?, date = ?, hours = ? " + + "WHERE id = ?", + timeEntry.getProjectId(), + timeEntry.getUserId(), + Date.valueOf(timeEntry.getDate()), + timeEntry.getHours(), + id); + + return find(id); + } + + @Override + public void delete(Long id) { + jdbcTemplate.update("DELETE FROM time_entries WHERE id = ?", id); + } + + private final RowMapper mapper = (rs, rowNum) -> new TimeEntry( + rs.getLong("id"), + rs.getLong("project_id"), + rs.getLong("user_id"), + rs.getDate("date").toLocalDate(), + rs.getInt("hours") + ); + + private final ResultSetExtractor extractor = + (rs) -> rs.next() ? mapper.mapRow(rs, 1) : null; + +} diff --git a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java index 4009fc880..caea69919 100644 --- a/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -10,6 +10,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import javax.sql.DataSource; + @SpringBootApplication public class PalTrackerApplication { @@ -20,9 +22,10 @@ public static void main(String[] args){ } @Bean - TimeEntryRepository timeEntryRepository(){ + JdbcTimeEntryRepository timeEntryRepository(DataSource dataSource){ + + return new JdbcTimeEntryRepository(dataSource); - return new InMemoryTimeEntryRepository(); } @Bean diff --git a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java index e1eac20fc..b7f88767f 100644 --- a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java +++ b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java @@ -25,11 +25,12 @@ public class JdbcTimeEntryRepositoryTest { public void setUp() throws Exception { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); - + System.out.println("data source uri: " + System.getenv("SPRING_DATASOURCE_URL")); subject = new JdbcTimeEntryRepository(dataSource); jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.execute("DELETE FROM time_entries"); +// jdbcTemplate.execute("TRUNCATE time_entries"); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } From dbae6c6d3082d2c04478b692bad2d2aa87a3915a Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 13:16:39 -0700 Subject: [PATCH 17/25] Updated build.yml with correct sql script path --- ci/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/build.yml b/ci/build.yml index ea12986b1..34f1ebd73 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -27,7 +27,7 @@ run: service mysql start cd pal-tracker - mysql -uroot < databases/create_databases.sql + mysql -uroot < databases/tracker/create_databases.sql chmod +x gradlew ./gradlew testMigrate clean build || (service mysql stop && exit 1) service mysql stop From 355beff09ec6624c526d84d508423f5b5f91c871 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 14:16:28 -0700 Subject: [PATCH 18/25] updated build.yml to refelect version in jar --- ci/build.yml | 2 +- .../test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/build.yml b/ci/build.yml index 34f1ebd73..dfb0f9086 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -29,7 +29,7 @@ run: cd pal-tracker mysql -uroot < databases/tracker/create_databases.sql chmod +x gradlew - ./gradlew testMigrate clean build || (service mysql stop && exit 1) + ./gradlew testMigrate -P version=$(cat ../version/number) clean build || (service mysql stop && exit 1) service mysql stop cp build/libs/pal-tracker-*.jar ../build-output \ No newline at end of file diff --git a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java index b7f88767f..87460677d 100644 --- a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java +++ b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java @@ -25,7 +25,7 @@ public class JdbcTimeEntryRepositoryTest { public void setUp() throws Exception { MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); - System.out.println("data source uri: " + System.getenv("SPRING_DATASOURCE_URL")); +// System.out.println("data source uri: " + System.getenv("SPRING_DATASOURCE_URL")); subject = new JdbcTimeEntryRepository(dataSource); jdbcTemplate = new JdbcTemplate(dataSource); From dd90e71c8ccb42ae516482b01da4647ce550664c Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 14:34:02 -0700 Subject: [PATCH 19/25] updated sql service in prod manifest --- manifest-production.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manifest-production.yml b/manifest-production.yml index 340b53421..6186466e1 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -2,4 +2,6 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - host: ps-pal-tracker \ No newline at end of file + host: ps-pal-tracker + services: + - tracker-database \ No newline at end of file From 4e07bdc45ff76bfbe8d22cebfccf5a2e03b26745 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 12:28:32 -0600 Subject: [PATCH 20/25] Add tests for Actuator lab --- .../pivotal/pal/trackerapi/HealthApiTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java diff --git a/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java new file mode 100644 index 000000000..b3eef23cc --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java @@ -0,0 +1,38 @@ +package test.pivotal.pal.trackerapi; + +import com.jayway.jsonpath.DocumentContext; +import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static com.jayway.jsonpath.JsonPath.parse; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class HealthApiTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Test + public void healthTest() { + ResponseEntity response = this.restTemplate.getForEntity("/health", String.class); + + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + + DocumentContext healthJson = parse(response.getBody()); + + assertThat(healthJson.read("$.status", String.class)).isEqualTo("UP"); + assertThat(healthJson.read("$.db.status", String.class)).isEqualTo("UP"); + assertThat(healthJson.read("$.diskSpace.status", String.class)).isEqualTo("UP"); + } +} From 2c9bded71d20a3821876ec914e1fa5c8dd2065b9 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 15:35:42 -0700 Subject: [PATCH 21/25] Updated actuator with actuator and custom metrics --- build.gradle | 4 +++ .../pal/tracker/TimeEntryController.java | 20 ++++++++++-- .../pal/tracker/TimeEntryHealthIndicator.java | 31 +++++++++++++++++++ .../pal/tracker/TimeEntryControllerTest.java | 8 ++++- 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java diff --git a/build.gradle b/build.gradle index 9a3205ece..69b98a653 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,8 @@ dependencies { compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.1") compile("org.springframework.boot:spring-boot-starter-jdbc") compile("mysql:mysql-connector-java:6.0.6") + compile("org.springframework.boot:spring-boot-starter-actuator") + // compile("com.fasterxml.jackson.core:jackson-databind") // compile("org.hibernate:hibernate-validator") // compile("org.springframework:spring-web") @@ -28,12 +30,14 @@ def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&use bootRun.environment([ "WELCOME_MESSAGE": "hello", "SPRING_DATASOURCE_URL": developmentDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, ]) def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ "WELCOME_MESSAGE": "Hello from test", "SPRING_DATASOURCE_URL": testDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, ]) flyway { diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java index 1f460951a..189ad4408 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -1,5 +1,7 @@ package io.pivotal.pal.tracker; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -10,15 +12,25 @@ @RequestMapping("/time-entries") public class TimeEntryController { + private final CounterService counter; + private final GaugeService gauge; private TimeEntryRepository timeEntriesRepo; - public TimeEntryController(TimeEntryRepository timeEntriesRepo) { + public TimeEntryController( + TimeEntryRepository timeEntriesRepo, + CounterService counter, + GaugeService gauge + ) { this.timeEntriesRepo = timeEntriesRepo; + this.counter = counter; + this.gauge = gauge; } @PostMapping public ResponseEntity create(@RequestBody TimeEntry timeEntry) { TimeEntry createdTimeEntry = timeEntriesRepo.create(timeEntry); + counter.increment("TimeEntry.created"); + gauge.submit("timeEntries.count", timeEntriesRepo.list().size()); return new ResponseEntity<>(createdTimeEntry, HttpStatus.CREATED); } @@ -27,6 +39,7 @@ public ResponseEntity create(@RequestBody TimeEntry timeEntry) { public ResponseEntity read(@PathVariable Long id) { TimeEntry timeEntry = timeEntriesRepo.find(id); if (timeEntry != null) { + counter.increment("TimeEntry.read"); return new ResponseEntity<>(timeEntry, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -35,6 +48,7 @@ public ResponseEntity read(@PathVariable Long id) { @GetMapping public ResponseEntity> list() { + counter.increment("TimeEntry.listed"); return new ResponseEntity<>(timeEntriesRepo.list(), HttpStatus.OK); } @@ -42,6 +56,7 @@ public ResponseEntity> list() { public ResponseEntity update(@PathVariable Long id, @RequestBody TimeEntry timeEntry) { TimeEntry updatedTimeEntry = timeEntriesRepo.update(id, timeEntry); if (updatedTimeEntry != null) { + counter.increment("TimeEntry.updated"); return new ResponseEntity<>(updatedTimeEntry, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); @@ -51,7 +66,8 @@ public ResponseEntity update(@PathVariable Long id, @RequestBody Time @DeleteMapping("{id}") public ResponseEntity delete(@PathVariable Long id) { timeEntriesRepo.delete(id); - + counter.increment("TimeEntry.deleted"); + gauge.submit("timeEntries.count", timeEntriesRepo.list().size()); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } } \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java new file mode 100644 index 000000000..592a6cd0b --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java @@ -0,0 +1,31 @@ +package io.pivotal.pal.tracker; + +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +@Component +public class TimeEntryHealthIndicator implements HealthIndicator { + + private static final int MAX_TIME_ENTRIES = 5; + private final TimeEntryRepository timeEntryRepo; + + public TimeEntryHealthIndicator(TimeEntryRepository timeEntryRepo) { + this.timeEntryRepo = timeEntryRepo; + } + + @Override + public Health health() { + Health.Builder builder = new Health.Builder(); + + if(timeEntryRepo.list().size() < MAX_TIME_ENTRIES) { + builder.up(); + } else { + builder.down(); + } + + return builder.build(); + } +} + + diff --git a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java index f7c0090e3..f5fbc4a3b 100644 --- a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -5,6 +5,8 @@ import io.pivotal.pal.tracker.TimeEntryRepository; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -20,11 +22,15 @@ public class TimeEntryControllerTest { private TimeEntryRepository timeEntryRepository; private TimeEntryController controller; + private CounterService counter; + private GaugeService gauge; @Before public void setUp() throws Exception { timeEntryRepository = mock(TimeEntryRepository.class); - controller = new TimeEntryController(timeEntryRepository); + counter = mock(CounterService.class); + gauge = mock(GaugeService.class); + controller = new TimeEntryController(timeEntryRepository, counter, gauge); } @Test From 92de54b82548e8865dcc36834fe0f3f20c3d390a Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 12:50:47 -0600 Subject: [PATCH 22/25] Add tests for Security lab --- .../pal/trackerapi/SecurityApiTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java diff --git a/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java new file mode 100644 index 000000000..72099994b --- /dev/null +++ b/src/test/java/test/pivotal/pal/trackerapi/SecurityApiTest.java @@ -0,0 +1,52 @@ +package test.pivotal.pal.trackerapi; + +import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) +public class SecurityApiTest { + + @LocalServerPort + private String port; + private TestRestTemplate authorizedRestTemplate; + + @Autowired + private TestRestTemplate unAuthorizedRestTemplate; + + @Before + public void setUp() throws Exception { + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + authorizedRestTemplate = new TestRestTemplate(builder); + } + + @Test + public void unauthorizedTest() { + ResponseEntity response = this.unAuthorizedRestTemplate.getForEntity("/", String.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED); + } + + @Test + public void authorizedTest() { + ResponseEntity response = this.authorizedRestTemplate.getForEntity("/", String.class); + + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } +} From 24654242d0e2a2d8f17122c152e3ccf27d64a854 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 17:14:31 -0700 Subject: [PATCH 23/25] Updated with security --- build.gradle | 5 ++- manifest-review.yml | 2 ++ .../pal/tracker/SecurityConfiguration.java | 34 +++++++++++++++++++ .../pivotal/pal/trackerapi/HealthApiTest.java | 18 +++++++++- .../pal/trackerapi/TimeEntryApiTest.java | 17 +++++++++- .../pal/trackerapi/WelcomeApiTest.java | 14 +++++++- 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java diff --git a/build.gradle b/build.gradle index 69b98a653..776f7d975 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ dependencies { compile("org.springframework.boot:spring-boot-starter-jdbc") compile("mysql:mysql-connector-java:6.0.6") compile("org.springframework.boot:spring-boot-starter-actuator") - + compile("org.springframework.boot:spring-boot-starter-security") // compile("com.fasterxml.jackson.core:jackson-databind") // compile("org.hibernate:hibernate-validator") // compile("org.springframework:spring-web") @@ -30,13 +30,16 @@ def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&use bootRun.environment([ "WELCOME_MESSAGE": "hello", "SPRING_DATASOURCE_URL": developmentDbUrl, + //"SECURITY_FORCE_HTTPS": true, "MANAGEMENT_SECURITY_ENABLED": false, + ]) def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ "WELCOME_MESSAGE": "Hello from test", "SPRING_DATASOURCE_URL": testDbUrl, + //"SECURITY_FORCE_HTTPS": true, "MANAGEMENT_SECURITY_ENABLED": false, ]) diff --git a/manifest-review.yml b/manifest-review.yml index 897eba97d..d853241fd 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -3,5 +3,7 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar host: ps-pal-tracker-review + env: + - SECURITY_FORCE_HTTPS: true services: - tracker-database \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java b/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java new file mode 100644 index 000000000..ff6087ecf --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java @@ -0,0 +1,34 @@ +package io.pivotal.pal.tracker; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@EnableWebSecurity +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + + String forceHttps = System.getenv("SECURITY_FORCE_HTTPS"); + if (forceHttps != null && forceHttps.equals("true")) { + http.requiresChannel().anyRequest().requiresSecure(); + } + + http + .authorizeRequests().antMatchers("/**").hasRole("USER") + .and() + .httpBasic() + .and() + .csrf().disable(); + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } +} diff --git a/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java index b3eef23cc..c66d68586 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/HealthApiTest.java @@ -2,11 +2,14 @@ import com.jayway.jsonpath.DocumentContext; import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; @@ -19,9 +22,22 @@ @SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) public class HealthApiTest { - @Autowired +// @Autowired +// private TestRestTemplate restTemplate; + + @LocalServerPort + private String port; private TestRestTemplate restTemplate; + @Before + public void setUp() throws Exception { + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + restTemplate = new TestRestTemplate(builder); + } + @Test public void healthTest() { ResponseEntity response = this.restTemplate.getForEntity("/health", String.class); diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index 2b7464304..5a6cc27ad 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -8,8 +8,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -28,13 +30,26 @@ @SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) public class TimeEntryApiTest { - @Autowired +// @Autowired +// private TestRestTemplate restTemplate; + + + @LocalServerPort + private String port; private TestRestTemplate restTemplate; private TimeEntry timeEntry = new TimeEntry(123, 456, LocalDate.parse("2017-01-08"), 8); @Before public void setUp() throws Exception { + + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + restTemplate = new TestRestTemplate(builder); + + MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); diff --git a/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java index cc7091ed4..ff89c4f8f 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/WelcomeApiTest.java @@ -1,11 +1,14 @@ package test.pivotal.pal.trackerapi; import io.pivotal.pal.tracker.PalTrackerApplication; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -15,9 +18,18 @@ @SpringBootTest(classes = PalTrackerApplication.class, webEnvironment = RANDOM_PORT) public class WelcomeApiTest { - @Autowired + @LocalServerPort + private String port; private TestRestTemplate restTemplate; + @Before + public void setUp() throws Exception { + RestTemplateBuilder builder = new RestTemplateBuilder() + .rootUri("http://localhost:" + port) + .basicAuthorization("user", "password"); + + restTemplate = new TestRestTemplate(builder); + } @Test public void exampleTest() { String body = this.restTemplate.getForObject("/", String.class); From 72079a71f240cb425ee4fad87ab25340e8aeaed4 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 17:21:34 -0700 Subject: [PATCH 24/25] Updated security env --- manifest-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest-review.yml b/manifest-review.yml index d853241fd..154310a7c 100644 --- a/manifest-review.yml +++ b/manifest-review.yml @@ -4,6 +4,6 @@ applications: path: build/libs/pal-tracker.jar host: ps-pal-tracker-review env: - - SECURITY_FORCE_HTTPS: true + SECURITY_FORCE_HTTPS: true services: - tracker-database \ No newline at end of file From 744e99223f395b541f2bcc7d414f2c93cca550e7 Mon Sep 17 00:00:00 2001 From: Shrinath Venkatsubramaniam Date: Wed, 6 Dec 2017 18:17:24 -0700 Subject: [PATCH 25/25] Updated prod security env --- build.gradle | 4 ++-- manifest-production.yml | 2 ++ .../java/io/pivotal/pal/tracker/SecurityConfiguration.java | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 776f7d975..da60ad3e2 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=tracker&use bootRun.environment([ "WELCOME_MESSAGE": "hello", "SPRING_DATASOURCE_URL": developmentDbUrl, - //"SECURITY_FORCE_HTTPS": true, + "SECURITY_FORCE_HTTPS": false, "MANAGEMENT_SECURITY_ENABLED": false, ]) @@ -39,7 +39,7 @@ def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=tracker&useSSL=fa test.environment([ "WELCOME_MESSAGE": "Hello from test", "SPRING_DATASOURCE_URL": testDbUrl, - //"SECURITY_FORCE_HTTPS": true, + "SECURITY_FORCE_HTTPS": false, "MANAGEMENT_SECURITY_ENABLED": false, ]) diff --git a/manifest-production.yml b/manifest-production.yml index 6186466e1..7aea4bbfb 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -3,5 +3,7 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar host: ps-pal-tracker + env: + SECURITY_FORCE_HTTPS: true services: - tracker-database \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java b/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java index ff6087ecf..9c325ec86 100644 --- a/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java +++ b/src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java @@ -30,5 +30,6 @@ public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER"); + } }