From f1c11281dc67e5c61a5d0c66cfdb6d6aca9c1eba Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Fri, 21 Jul 2017 09:26:37 -0600 Subject: [PATCH 01/16] 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 9dc8d86978da4d323f79e0bea73ec4ff393233c0 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Tue, 13 Mar 2018 12:14:17 -0700 Subject: [PATCH 02/16] intial commit --- build.gradle | 12 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54333 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 ++++++++++++++++++ gradlew.bat | 84 +++++++++ settings.gradle | 1 + .../pal/tracker/PalTrackerApplication.java | 12 ++ .../pal/tracker/WelcomeController.java | 12 ++ 8 files changed, 298 insertions(+) create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle 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/build.gradle b/build.gradle new file mode 100644 index 000000000..edb330bac --- /dev/null +++ b/build.gradle @@ -0,0 +1,12 @@ +plugins { + id "java" + id "org.springframework.boot" version "1.5.4.RELEASE" +} + +repositories { + mavenCentral() +} + +dependencies { + compile("org.springframework.boot:spring-boot-starter-web") +} \ 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..c44b679acd3f794ddbb3aa5e919244914911014a GIT binary patch literal 54333 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
K7|BUm`^7>V&Zk%^_d-%A~k@lFe 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

Qox z0`xkkAPQweAlfOtBr;PCpCyY@I(B}_q2#9zd3W%J|3eWKpVLA(TO z5%Zf>!cM)^YQ?&n@bvEeMq7qf)_Rqe86vho+bO6^&4TNMJrCK9V`zKRuXfd8M5%~s`9IYm95q_DwQl# zw{#U3?nojDov=wtw2sQ^BnoussoxlxR&D21ZG+h=hHHPRxddwfoNLfm=2*#>S;;QV z!b3X2P@Y~tG@ zEsv?a$avqb z!A;+xKmVyOCP2?u_M?6ro!|6p3hE1XWYaW#CmFc3%s^$13Jd-mV|FHKD;5_gD8=oL zv9{Lt);bu_WV&2XT749?b+HvE@zDP45=p1BaTTD|Ujs_}Pptcu-!Z)p9f!fEsGcW0 zNI*A-;X6d73JsXdwnqOVLo}*B?BqJxV>?b(wQd&e?en)d{)G}U1e&OCD|aImZ`3H6ub*NDlQpCW z7Fvb22s61l4U30fGmyZE_9%KpbX?j2jtpKREvCcg;qd6)+bMk%rMajuBY7%4@T_MqDUPcc-On;3{h}TDaHHiD8llM)Y zenv30d7+wIdgsx!>bknt{ArjL-`i3>%>zm7b1aEWPdW0}Dn`+tNiz|#nDU#_Mw2GC zF??~VSmm`iB5JmNJnfW{;S|zFTxex&mW5Oa^r*W%uJM>*pmo=TO24r~ap-AG@Z^z& z@ag%!NpczPaLM}v-G7twO{k8Y@*^M&%;gdP$@biw`0`qQ$SNmi*8mkopTL?V(*&}c zBLjqsFZ6T@g5&L+aa)+Qr61|;9SRLU@j)Cb*v4VnqP&h-Cqz$)nB3x)s@C4u!g%pM zEyb*^R3|r3{4MKBUPH?(D8W81Y2Wi>?d83MZ{MQ=!DaVyWJQG-->ZYzQh6mm-2RAr zwJeG0GKJdfJyLuoeXc_f?Ancb`$9pUO=9Ebr%&VtFna#h@=(gm!2vLt`(x|`>{9<} z;LQAwbHwG{$}BQEX-KrBUk$h+Oe|hb=vXisNt!NgrwZ!qNZKii4fNz~AIrU&Cthe& z52`m1Pr}7=!w75=OcL=4TjSp2n8D(|{FJg?rBNVX+2cqF#nR*srLf3GN^A4tb~jU^ zw^00dk6n`pHdS@eyf=nvnjNK@PwmDHX|tg8hQda*<{Z&cN~6kAkK*PmYn!Yzdc&qo zZRN_;yI>xRqWF|ahf0Yk&#(p9mfqqvcEXjhG7XuCqJKPLZjihSvsrMYmv?GtZtpBC zygaAfZLcR?ncPb{QqRN2JsWmcosmDIY;l(-I{^F9WE4l-zK$g{sJwQ;rCrzj0d1cdA`jz{$1?pXrG=acA{?JbGvy(oh&ivO9cX;@g)xX}$b5Kq948PdDBiJbiYt zR0vER&T`jt{Dj;JtKbTgsy#L^0Zs{7FHT^NL1-580djJX)=Wk;e1aj-1UzILng@P` zgo%F__Zz9(sqT9~vJ}FxsRdQtC%d@`Y#?J>qrJisrL;3PxBXf$=g6%%F_Kn$wT!uy>CK@uaU z0F>zhy{(7o7W{}c*oBRdoE}3X9G68iyzT}{29wew58xymHl3&f zuKG?e$hb&uX*2Ki=|a54*X&bX`B`dyny*-oDJu~g-4!B*9?~JIa+lH+$w8>&CeB|M zHvac;C8+@GF9lftZ_OM3ZT2pD_C|l3H&!SuSWnBsak1EK_1KA#TB#1nPbCna#xZ|L zpr$O$`yj6vKXAO9!cL#;+Jqw2C99vUJ7z+5)ek$x)ON(BhmLXEvqt zE!l_#8jiyN2{>H4nZuoy$hkMW7~ZA(&|1LI{Yc%}K>^G0u+8Mhn>+&O@;9PmZ+CBO zd<`V`uQ_1;u#fK2XLP6rV;~bO>TAn7O zQMZ>EM(ELT)0mClcC7IkY##L4t!cV?uT^+Uv(ezz;AQS!p56^|2ln2^-NffhZ58{8k5t*V zK`^yH?32h(0seh<&w7XO%$Z1y)w53NfD`s^S{ugGPuHN8_N`V=MyaLW6}=7_9keUc zvywH`bHX{CBFadUFYkPsYx=p;Pq^#j9gMo|hCtf!oZMZ6X~|VEMT>W)6bPXLuT2Ap zJ%ZZk@$w9(`$o7^Iy-RnM@|Xu={|tY$Y&YlR*My=zA-==mW?tW$O31Vktg8KK&8c| zt&F3QqchlLNVw7JK-*T|@o?4G%0i>wMA$*6Ho#wB=#~XnqUXjFR}?T@Q0ZC4cK~uy zai|eukdf#KcZjRHEmS(8y5K?=Gy&|vDh_o+kTdxq`%T@zMMso0AuN*p|hGHue ztCRZL7%~=DgK+i8FgEJPi?!01K5?H;fX!C`Y@X$J)=Gca{L9sQqSC)S;ohgSlXA>x zl|!Cx$o0kf70i=VQyK_; z&K^)rtR@yP*;m_RzF|SzbaP7PBWHUc?&b|#+I6n2Hfgbm;0k9HKrS{`Z4Dakb4dY*Nn57C#) z=ECn}*Y1u~%pvL}>{5-!9ou<#23Q+=AWl%|Fh%D`@94AW$~9{*_^6gdOv_vO&i4#0 zi>d7wf0OY^@!GR6z5U_yf%%@H zb_*}SllSF=(a5w$dA9WgP&+VDPtU-lb%--Yg=2F}3b)WP0VEyFbgc;K0!u_p1{4rl zuT+SIC>2yD51g9c>`p3T&p2+oQL(5e|2W(B$-NV`5TnJLPXMj)X95zlFc(T zV;*6TyX^>C`K+kBi4bGJ>i#^BW(A^ z2R?pZE|5he!8_?UlcB|w%_0M@^j3}-P=KiErPlGVW3{%4&fPv#IAO4uW)`Fs%HdX0 z4uXay5=!}E#1_g(zlx6i4*S=UAd|qct{89ztmyBuO26J4`s1zm+aQoAuk}+_iK|wv z)>%rbE^X5#f=rmq8cBx`-;@{04=R@PmRT(5WWZS2n1skDm#0`Jkoy++K0nNb`4v30 znKSlSX6s(oFqg~Iu@@rhE)gMy+y%s!B#=XC5lrSbcUrKR$z_rHy{EXWQk4a zmmK_S-=qaodySWOuo0Yn0BnhzJa^IL{EV%fVr%SpfN3d4*xzu`(i-(9^dQMw_P_=J3AAf)c! zAse)jx9GXO<_2en3`Uh-2z8`DF&5mVd9kgOIN~Y#PHsnmFyg$b8z^Yy(D02 zoKEp6SSnKeg4dW0^j?V;Nn5Msgfom9_Ra|-8Eq(DM2}Po zznRFri~2Y@(7*&=g{uWLz>v=P+NbkQ%-4S*!O-i6?^~ojVUXKfh^9Jb%7Ug488T`; zw%)u^R7wXUN^k!Ch~9-yz2O91qMVV+)k#Se#gDM&Z-nT)& z`UYdx9f?)jAU1d0MkwkmwszZ9x^9G4YoBv2mCTx!u*`eK7){fT)5EE;*$DjXHpwDf z+B>rK9jC1zCQ1Bc10wytMU7r7OkgF~_?uGdw*u+T705iMs*&&Kw3bSnqm-`FrA}vr z!W%guPH=rNWM0$5a=0G^P$m1Q?MNLmXp%Z3rbRtARBplpqpfO+n%Hn7vqA5C%b-Qp z+eQD1+DQj-rcg*QeYitDz0(!Y!KC7r^cItL6*ZnfuNh6R}}T(~1u5O?VNB zazm$B2ZzJRrqkk@@!TD{k*wqsa-1eO`MW5waLvX58*vi*Apt}OUQ@w(Q1@!D(UW>e zcO0zH`fRacvP`=RNHEB@r>%OdxQEbG=|2&qN@3-lQ4o9cuW<6K2YgR3sl()d2)fvc z^ksPGL6UJVNL3_`?cQoV;vZTJcT;DI>_PSo?%u7+8!E%x9~O@p)qhSD8#35D$v7(K zI6H7FIw1XofP_Jo4t<=rHzC9K+?pUdAhr){`9xQE^SUL8+nAY5f+8iU;k}(35!A}5 zm!^M^MqQWaj~5xVnv+C0ya7h81TgadkGbxzefOD);{eG3q$gwNrNF|#Fj-_Od}ULz z8YDP=@sNU0v3OxgT0-}CLj^Eu&V#2(x0Rm<)4@G1UWXF*)%qk{j5g%S*Y$OeJ? zrF-59F#A3AL1aYzc$qfI_b6}LRCM2~8=I9THdQ0E{)ZU}7FdO>e;(H)(3iSoVHkG|S#aj2Tq z13192TLHUM^uIHq{rjM;u=Z28^GTWv3EBa)vBW`cSytEb%bhW8nkXY3-V(wH_O-Kb zkP}(sZUe(T&)sG?G50O_tqA(K)qYg?c>VH6H#`}x6q z^DW3M^$!}RaP~A_2mO^0sqR|=y3Sp>BC03%Qygt*H(XbIm%!HvtsA@`B>Z=aS*)YC zBhe6n2D$h$SNia^wYS>hGET4Ig|KlNT5>U(35bGx_ujl-I|9FIiUn z%A!qX4=Gi_*^Yx@ek2!es9RP$&WoWkyKoO_s3fM*-ZWPXC|6kr#%W@9iJ6;+K=B8_ zgLBgb&2+wc=YH{yfsSfL79Qm*NZAv+`Eg?!%5~Vh$RK}sRimWG^2(=ISXblie3Gsm zkK2$-;pwf)lq+C2v?v$rk~-@{_#m}iJ}PhSt9AF`&k?MvcWSmHaa$jN`&g7=<{wAR zNZ3fLv?YO6KfWer;3IoQUMtDBm|b|oLr4eVAU1OGL+}d=m5|f}Yjo!b6}I*bgVH1ubk21&MUkV)QN7<&uymkUFE>r< zRJC!XLc#MB*=_8uo-W;Fba(JOkRc)8K>If?}tg%gm)QkX(fIQa|paNyJ8fcJnWvT2Uz|@W^8=TE8K%hO4V={C$dIW zk<_T%6h2)427`Bs0W+9r@(4Pvw#;mAk!7(6hSdultQxeDKf*0j9hHq63p&l*E(FHq zl~K*c=h162i{3RX9UFFpLROYIRdmX|o1R3iy^YjVKc=N{?5{iTVIC(6EOWfq@NLSw zX(u)6dvXRcHYKWnVf9zj!?PJ-8WU%! zdEZM6*bp}($=xSOM%u!x2^BAKOZfSc!}MT;t8+GqQSzI5X>Z1-J85T-mVmxY<0e^& z7~XF%qlW1*u9!0frNO=uAfZ7yv-Y6Y*;5X@{vO#^|7xb1f=&>p>&?AtPz(}mu9AG+ zz|9w;ukfOIUX0b>>nJ9vB|CHsz+>vFxdQ5rvAY&;vA40ZJ@E0nI_}!cuNc>j zSfe|EQlVpN8lnf%3D(b?beq9Cc!v}_9kvVOKl6CnmZr&i#72Zag{PpMy*G}v??HyN zO8&AaWQrqa{}nGEUv*xlXQ8qs4naxzP?UxmT=QK4?m>78a}pL0&=Q;c3^)#t!f1&S za(5yxVC4v$X(0N*9uQ{#cWj(`#rCG-Fy;-80sV-kOj z2GWhcO2{(!nHJH6m|ycyyR3e(1*Lpu%Di-DmI<$Ds$;f-TjN3dA?wU(@|vonx3EIX zvO;F{Y?*^0Rg9YWI(pgRlx^)M)8_linWXm9eri4t%5Z%1yno}DEvqY6k$yKOSQ2ZhtlABUwteQ;g#Dy+(+fYbu;gkjV3cE;=xrY2}c4kOd}3t7r&sENjgXy znUD)|0haHPGcN6??4{G-@)Q3IDSjGyXcsp%y_+6S;$Vc0b1NIKkL6@vL;TH&G9EN7 z!BoD~ATT2@UmJydh+b;QsXQ08fM3Lau_Rtxs?@Q(n71U!?Nv#xN`dkTB@}L{v|2f~ zgd>}hv_frR+Ls-@{0!_EqclpDX?LgXu=nMP?v+pj=2soU@eGc2WSy|LF$`+MaHO@1 zhDpSL?PBePnGXhy870Ohpxc%^nZ#OSu?|iPxTCMka)~2?Ex#DWTfP}^Gp|*Or+N($ zQ6$-*5s=d@(4Fi4GY2wjvX^gYIPH`g;WZpM7$N}#q!p%7H-OJ%`!2m`J3J?&cy|* z5T_-Ly24xvz21zOCgLSfhT}vAfoj*h`pQiA69$4zq^jA&u)cD-qqJjDjvT#D=(ROt zD`W%1>hrz84DCcI9d^@6MUhmk8W?HsTx`teYYH#gQ21=SvA-eIHqgLB&GnUAAMu_5 zhMo$13J`_-s2Yn01^OamS(fznfc$a!R1(H;*&bty{za2&E=b0lC_ z%Vjwk`jnU}N?NVHPDWvp(0-JcnKYG6Qh#}3(WtM1l$&EKP}dD(!(@PWm8E$}?9QLS z`NQCgQ-+k0SGzeeYrAE?tH*G^c+~!3-FUc{y4k0MjiyZnpTtjL z381SjY6g#q`z-qOVTxHSg;*tz&@|R@ zbd<#4L`k4$XfR3evmym5l>K0ejVsGDFsJt0>nQEKmyeC%{8MAi_D_t0IFy7QY4g-n z*$FU?>hw$S?UfVN+v&=N-w2r(;tEv2<~B`zshv9{vDDNLdT{+P9!98t*glCKUPD*c zqphqt*%2Vls{*U$`>20h>&v0hlUialwQWKswd1Mh?w@ax?Z#WBTMn)@-DnuW*N>;M zVH~ss-kIoe(1U}Z!hM!y8iL+XL+S6M#faI!ejL(TSO=|o7xF|tkSf|x?e#X0bh(yg z>p(Vw%Re_n;~=SfZFO#@P@mpona|<`%Ski&e!|2jR0Q;6xol8{U8AU#^wb9#&B+7# zFQZX!D6nbNT1;be>MZr)NcW1__de&zjTwb~`!Z-7WkDm4pF{!gn`r3Jap-PQM>E@r zEtY#WVi#wgfC=2Vi2}^BNerB=P)oDU%s;gcZ<2n2jh#PeEkKPh&SCM{xw7IxXc4{r<4&%*uV_Gv8Q+3Qhh%eVQI1h(0MS(iKGBXp@ z6JVyswUL`@^?^OSq*zJitjTufqqxBRw!Q#$?Drtd7;gdU#Nm*4Mi!epVqr>5$U&Oa zDx`Tb==O!0LY8$mGYyNqdv?$sY1`^oAJd?WeZb5M-Rt{QDKQwf%?mHfFM8pjTuNKu z7o8$CEe4$I+wroMqnh}r8MYbh^YK^)m4ZA`8qw`*J*DF{V49W0-o5*5CuTLUw*!4# zr>QGXH0V%>g7BeW@*(i+snwxfE1t_hCK*TkJoJ(gf>UXGAraOGZ{L=Z)JR8}tY#%UPMNjFrCF~oCZ!m7FJr`mg`l^aM7h@ij z`rIV83S-NA9C9XNDn-Ar-F~HH!LY(76AzC39mvBsLOCR7 z)+%U0;re8Yg>L1nrq@oAMq3p_M-?*+HGLz+$oU%8<*UZKYIchR6de_7?}31DT)og`sIzEIud*k%-vx2vN1K0@Qi6W~ z;UFffX2pQKL3I%%fMh_*&1>f}4%qGC$Lhu6icketpd5QtG+F3A4P?SeuaZ7zx=X@~ zCKHk-Uuxd{n%SPr6hL+phIOEJb*hED6U0d^Gf{%Li{Nq2Kunl+&fV_G58vOaEOL3k50-xR_JxGz3#Y-H5vu<;srb1&&Y@gH4W^p5(6H zYqP+udfjjY@l`EIZ?#>cWi#mhN(45K5!Y}hT)iK^XQYGtXo??=q#HAZ5cqwZ{YJyvsQjT;hwxjKG~P+9F4rG?~i9wQJmdgjF*-( zOV#UgMn!x|viNZH7UgcRJ0boAhZ;p{Q=4=5sWK2hbM}=J-}O`hG4d9%%e3P=!DD-b zawq6f5-tv!JEhR=BN=H*?t z_If)wCJljVi(fKcWW$QUpZy|b)mI5IbrJgh@AU!gcp?`)tZ4}QT4zrM1D zE^&Zn$mLu4uCz*((eyPQogGX~UWdVBe7qZ@Ya`khCn;Roe~M+_OpWRE5g|4^@_m%R zoW@0zD(O|NN@dG1jl;ztVf*%)#nsa3AkK;U9}=gw4u*gIDpO$LEZ>?(An6fYs<8;*w~0zLKZkzj`%#s4Dw@oz-@WA&41ie9!O%NmtJ!8VqLle z{mt9ct`*G6U7`ovlEgM8Ob6CoWkqaX=8(?@W_;f1C6g$$(|F=gvb6$D!4Eo{%flDi zPZzsm`D9-lP)A4d(as?3mxOZ~l{f=4^tK^`bYb+wzd?LmA}=+BP|zR`miv6<$Fh&r z$Joi|CNv5Ky4HK?uH!Vp5`qrCGnrFaWeUgeHcuC%b`k05IO$b$@^B|#hAkXP4E;XA zMW{b($tup}Tm3hX)Fhpn={dyv6sk-iZcg68H6cj7Vam|vd>w8yHEuG*(`trkHVm1T z)9zkk@?o&|k7g}yGP<33NU<#eUxH&;{N#hS63$`*1+Tn~oF{l90@*HaB#DNzIVWe| z@JJ1PoU;_C5_5C9f*2zG&{m}nml)P$52s|#S;7qm1Cw`;3+3;d(5wi`QnHhVqN8Ok z_t9SMM2|9G$y31@dG2Td|EfTgi>jt*r$rN;^?Dg-Ru*+ok)@gE{Z#0sykHAfjSv+u z4pk|3&n9`I3^qr07B6ykI$e5T6;OrgXOs;8Z+FX3h)Y$ds5v-RO$bYBZ#Yt1I4*#k zH^?+YK6P6^qM>e}7I*@mxZ+^321%#BmN3qh*v-)hnXoyI&rBxJASagLZ9XcZpD)C$~!S=cnRMT(r0mO1)9 zVyyKv?tkl-542I>%2KL$v(MRi7k^m^OeN8rN3LCV&J8QmOA5E|e6hw)WIf7@NL3PG zJEIg3foR7ew7h}8Y0fD{vxMIxG0ODuM6ro3fM_(4YDVO!EsI?zwsOEDg-C5%L;kE% zd}g+U4Xw|NZQeOE`tHGfhBgUGy%dYKv;2@S=?hsv2}aKWaQ|vK+UVfjCG&nVkQaUO zZGDIVmO)i2-D+Qol?hB@2M2m(^9V2rIXi<}$n759e9{KQL0d|YeBT}|)v{!m9%pyG zQi?(Uh=GKt-kx;C{5-nuuFt#iDTWeJHVP3d67OK~CF~2!0?xdWM_Z8LMe^XPjB_;^ zRjo;3Bu%yeC8`-SPpm%k7JU$l{T7D9_L&Bj!%#gjpSC<>vEW-QI#}@$^|0#L801gX zM21{}j5Re(BI4GxEM!JyX+(JHD!B4T?Kt23U$I1>_oX5+zjw=D6548v=0bx(%5nlR z`G!Su*&opq)w)5Qx>|rd^P9p0B!#I!d)O0^bsXy4MT-h^B&an zT&hJ+4N@_Uy1qvoTuBrSrAubJG<|(Fy+hzB|R5B8)Q{XHddbNgL0yaQ%e3oTLY#+!pzjN}(n7xHrUFzGr0dTGZJVThU%RY3H|s z;hhqPbHCB*&=#2U@o0BexSg$qAXx9Tk^13HJ$?fgy+@(P_ZI17liCVmndH!8+I?#b zI}ST+ZGJd45Pn~gyai!7Rq=1umAa~vlei?>l~POc*dp`u_jn4f9!3009cE>kb_ZC~ zk}edI3O{;BN?r4O+7#uo9fMdz8#x(Wok^tW(s3ON3e!6tu#}Wdvy?paa(IK+80Nd$ zTp{jt>|By+a`m}-4s8Kiq_a>sk*XfzTrrbmcZ;d3XB+~Xhh*Z>kM|q<*!rF&RlR9X z<%wx|5wntIqjvYFi3Z#~v5CFnuR4R!9@h@{%ALLH;&((;6J&c%_>N%vOP4mbjyX>% zAPcXuHr`vl<;pMTR$tI`a;z^N)7Z{*Kzk?)Ym+$iVy?N5YZtWzX5GSkBD@^_m%|l?>l8;#$nbby= z70Hd{fj~Bjk>1*e^F+WldSI)>1)sXdZdfiyZ5CwPf~g;|lO4`59z(I+vlFjPW`F3Y za^V!@dV#rHn%>B*DlymX*?I@Uo?zeK$-i4{-_F$Et4|)a7Q2$+pK>@8`Y|q96rD>#oIDVK*+lpFDe%FLJ{&`C*WK`Dwpi&zd~f zGP*()xIf$tKFlt{L9>&tvpRZy`brL)(|KE&8Zr2QQR<3Rds1t;FT=Jy+!Z zGB)k4(aw6zN`miKm^@M~k+%feU-zDP{<>kR;cA_d0Pu_U13Wyx@b3J}!EX4cAm@MY zk*X~Cyi-Ab5?&gZ60BD0k6IyCnr2NhVhbXia4iYnB9_8jBC`{-Rfj^fz?X?JNthf6 z)ex7+od_%}1WilwVhHywV1y**Nn*LZ7<*^acCG@~!NGtbG228(1K2rbyW!aLG-;mV zd3xyQ0luYOmB~R2f?@E5i$K|yOR^*L{m@#~laJpmozuHgLR=j%ET-96NT@5rt#miKK(YEQbW#J;BlX9pFw&ERcRz=`p~tW>_eq1$YOWBx#9 zN%&zLyK4Q_)OwvdcI*Uw87l0|NAOjTLq}`!a$2-^3JBrAerFf{UA} zSV~|uhRq0VI^@^CF%hqX*l&N=z}y)Xc^G6JEw(>0OO{c^B*CRKC44_78X}njD&;zU zZd@9^$8-dFA;s@Ll%XPFq9}oCVN=_?lR+H7A1?)o=T#YS&3=Yy&|r3vF7JHn2%H$R zLcS8weK~f#;7TmYp-;1)2(&`%c{pSod3}u=MCiykRi*h+&W@GW=koM4v@Qa~$UwqG zsBg1DjpFv&Qb!gcK|%?jofFwrPy(IjACKcmuY3_>r1Amcw9L8LTw>px-L{}K87fV* zqFg2FKsiu-iY;~_=lnH=qvLRk?^6TiheUO*lL2On%gOXv(3!I4Y3t%xT%mg5aUdGdG4GpU1!wY>+`;RSnI86o zn&Uny$$U3ln5%0R16umR-^s_BpH#X?d|9iRFL8QZ zY!)PEdakEjt$w%OpvCk&ium?>ml|dx9vGao6TEN)&O9H zQ?(!L)@p|}xT>8Z=W^&O$Zh^EMxH92H|JiUJfGhZ8J_O4Ff=eJBRxX!BwZjf_XwXF zJt}sNpF2Q;x7)F19F%M`M54yF&bexYwu60E*rTb5K^|F8@I!v|QyC{#@OKg7&R7QGaU(D2C`GEb(UO4cZ*AXwIW7Z(dm!` z%bC5Z{ryOc26$!#7F~wW7OhJtp`c&p(Rfw^n84|Hgca;NSPNyMNY?2G+XnPDHnS%aNeG)n3MPjio~E`y@mAG3_QpCxUm0pk2@F4T>kS0 zkMNE=0&l4MJ>z>?!_&67R!}mRce%|P5No^v`U(2SVB1b~($#_bn_zL@Eo2EL_vWgk zx|A{sV&cwKEVG*c3U^oZZIq!^p zQa){S$s7xy>)Lxo>gmj|jrCZu##a*0GwMRW?Lim%KpU=ARsD`;)4MGIMw?hHpVKbm z)q?1TUIboH@npEjIo(r#3ehHO6r4@mEuw3JGj>;hW+fiqtEf%1bep)SZFsI}9v z0+~%Q@eEAQIDSt*?pOyI{Aydc2;H6`-Y9X!Xn%!D^ype2xdR~GH?f?)yNIn24Thn(7Y?{F`=H!D(JNs=g!Wd zR0k-q7s!&r9NtR(8?)@eY6k4KFjGS(z(eRR^M+y<&HGSnEngi>cwAW7eyN%=249!{ z2GT#zh{1d1`tI~{L!tcUz6F^h``YX~43W08_9Jp$Kwy-q0DCKo;GTN%H=ph?fLBxE zESz;_nFi__#r;Q|PUT`9qMol*kz5Ba2`VND1GR3Z05uO;L%C@?+|IX@n-mPk6yUr( zG3Exrl8;1r*g5Znd}ShqS4gq1YCb@~3C9{;X|Bb5rk8k-*Lsb-#s@0Y&qoWiBEZ-N za%#P9B%GkNnriCBNLO`gGX$wFnL%DW6_-tgf9vebF)eCe0NKBXq>d%tvk-n;Zt?&- zel6=`<$Qk?Ur~nBLSGf68L~ts*Qj|J+ynA`&wbyTj;kB*j&j8o>xPUVvlz-o~`P;bu$04{sM)ybs zjei{pX=tQ6!7tQA;v+@Pr5XxDZIdknp~ExlDFE}g5#Ue@vUEvbp@R2;8Yk|!%?TBc z5%jtSY@{Dk7b1yyre?A|WS)7hu`zu5;rZj0E<6R9p{%T&B%UAt+k4vVyq%!1bTP_; znD<$IRFuSa8s29gnkYWqY}XWQc7%aLA$W{f+Ntmr)eK*!tbPqBQ3*JrqS!Bi>ekmD z-heW0@lN)u9i$YfbdRcv*r6{Z6z@XNR^wyTnOB6 zXEL^?7a4Fsi*V!cOW3ApFxU_3J|v#AD4Nir@87vnYMsWKZ z*`{%!koSx#jm_z`mNbps*RD}&4{Sf^DF^r!$g#~`LE<{cK%z5dbX7Gz}Lkv#VyIC(58=rn|46|`NWI5CJqqK-HiEk8*PI{qzwF_3*TLEM{Bh?mN9h+_K{Bp;)37iNA|PO;G)#!-5QEhjvSUQ0#}s3`f@CaW>Zkz z=8*Zn545hcPzWM0uy>MFy}C6q^_-sL4+@AhuekawJaVnJzkpRCxSzT|7QIh2CwaR} zlz=!37KLyT6&Qs{9;_4kVW*wvYBq$O6hD~LcQHWUM|>vo8WI)jW5s-!<5%M&ZE}g5 zrWq`#wfZ7hRi)K)4CQvLi2P+UT5LL>0Snl!PMsyvPCfp;4AbSxnv@ihOcxQZV%&gWnR5;M3Gz8 z3PeJg682V6)pam0?CMj3u^^~o4v^660+Afd9@%~sB;T!9;#MC`y=yA^a2VP6PRv~^ z>L;sUE2bT~O|M5_O}?b&S;MhD_A`|%Y2{E0`yzdb`{Yms&UUpfH~czuEN`<0Bb6L6 z(cyuHH%rL`Qk;C(p!$swGKGWx5CvTa)C|Zep>0veW!-z`Pr0cyj#QwdlzAK_rhtE` z^VFeAxh;;>e}MdT5&HH=w+a&AQ6d1YpUIj3Erkv^vHQ<5=sPdP&mkZn0M+>b*Khi*7y%G;Dp?tN(SKG#@^&_oIn5MKQ#cVF@Gwb0rx*^{96+KpQwKJ z!E-qR-2SQJzvb%x#(d_ko_q#qSt)255SuNTm;X!fIC${Aj~hI1px{HmNt5Z|B<142{>fK?(i1SO}v2iGX4dS z6W|&7CqTf+;p)cc;Fjizl8S=p@r85bmb1fWPTo^e=bXa zm+2R+1xOoPIynI3L4?gLjra@<01Q%k)_VV!P5mVSNQwK3CZNOR03H5U;|K%1{=Xm) zvDX7+a8v?F35wcS8A;mMSUB1Kx@(Tg&Etyz!tsoNmXbd=9B@a6{}0grPCNC}_I{1K zcdY7A3P4!`TmYay6&%1X_(hY&{$q8&#>*^2yZr*_V`e~ZhQH!LQvVvy+QCuJ((=FA za3*v!FCpMfy#{k8dyTa*D02neux`1wJ8E{|4=U%3tyl@F&1eEBje~ z<|uhCwgA9Cy;MJAcV1S0nX%-#a`xXV|0ik0f1eG$gnyZZ;u)UY^lxqZ5B%?}BwiA| zRFZ!t8n^r#(VyD?Uv%YP!oQS6e}*@*{wMesljWDNFO|TbVS8=<3HHx^hL@NxWo4f+ zN1Xl%^N%Qi|Mq2kDd75y+T{EjsQ+Eg^=0#4ic&rkNxALt}nsf=eTz3|_l`Ul~R zmrO5Z37(mBqJD$v4|CxArAWa`s+ZB=&r}MrzfA@BzS#a*+3h9C%i!8)60?NgCi&xi z{gd3tOO}^WoX;%ANx#kV=a|ly1TQ1#o(UvU|33--SC74nX?mt21gzR#jB$VZy#M>7 z_CNdTWpK+gzDL$?;Qw=|%gcUy84K`C)(lvW{I4JL>q*wj9q4DwzPOF^WS)0PCNf(M*m|Nf9ZL7 zrors`zbV~+^TYh7&HwSb{Ml*p)9dnFtN>vD%?BeZ0SZ_L{S5e{2hstYLKp!2EfCQE E17(ftEC2ui literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..568c50bf3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 000000000..cccdd3d51 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + 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..f9553162f --- /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/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/PalTrackerApplication.java b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java new file mode 100644 index 000000000..80f2a72a5 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/PalTrackerApplication.java @@ -0,0 +1,12 @@ +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); + } +} \ No newline at end of file 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..dd177c25e --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java @@ -0,0 +1,12 @@ +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"; + } +} \ No newline at end of file From a12c576e045af7dcdb9d8684ac8477860232013b Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 13:56:50 -0600 Subject: [PATCH 03/16] 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 364252ddfe1dff39c71b05827499c38cc8c88021 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Tue, 13 Mar 2018 16:25:46 -0700 Subject: [PATCH 04/16] pal-tracker intial commit --- build.gradle | 10 ++++- gradle/wrapper/gradle-wrapper.properties | 3 +- manifest.yml | 7 +++ .../pivotal/pal/tracker/EnvController.class | Bin 0 -> 1631 bytes .../pal/tracker/PalTrackerApplication.class | Bin 0 -> 734 bytes .../pal/tracker/WelcomeController.class | Bin 0 -> 790 bytes .../io/pivotal/pal/tracker/EnvController.java | 41 ++++++++++++++++++ .../pal/tracker/WelcomeController.java | 5 ++- .../pal/tracker/EnvControllerTest.java | 9 +--- .../pal/tracker/WelcomeControllerTest.java | 5 +-- 10 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 manifest.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 src/main/java/io/pivotal/pal/tracker/EnvController.java diff --git a/build.gradle b/build.gradle index edb330bac..8009ec0fa 100644 --- a/build.gradle +++ b/build.gradle @@ -9,4 +9,12 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter-web") -} \ No newline at end of file + testCompile("org.springframework.boot:spring-boot-starter-test") +} +bootRun.environment([ + "WELCOME_MESSAGE": "hello", +]) + +test.environment([ + "WELCOME_MESSAGE": "Hello from test", +]) \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 568c50bf3..e97ae74c0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Mar 13 14:37:46 PDT 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.5.1-all.zip 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/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..7eab781d1c865e4064fc1033768c5b213022d36a GIT binary patch literal 1631 zcmbVM-A)@v6#mv;Yy%Dur-4F9OQ@583E8wI4GomU1*f%QAhIpms29=L9-K++U2Att z8lg&kj6Oyy6-8~OK0qI;>Njf>uR|_SFJ{i)oHO6~nfd46KmP`>hOfskfjgtPfKSc# znb|%!+gcKL$B@9C6uub8J*=njWeWFG*hpeCiLWf!9naSmGNl*dNH{IwHl2$0mD}8~ zFy5AJ&wpJ~ZN(##^~0hYXyMjn(QU{dEnFOE@{NWsgTO*!Q@KiSS$Mp3Hst@)EFTc& zw%3rvP*Sek>$YpsuZmiWnoP;7i`Iegm67{OTR&ICa-*a?r=yNMEm}^8OZ%e!QuR2*$H7D+rv<00}E($Sfq$C*K}FcsCA?pw&7XVU38c;)-G7qZT0 z9bYo<>%st72rAy`(zYIb!##(M$N=;;{V5Coj=l=l`1wrmZ?c&@3qdYpY7tVFBc!Yd zX$%(Yb(wUU($AzLq;t79uyQLWh~;jbAf8)2ft{PRPcZUZ=rd!^@M8mJc*lnr#Vqfe zW0kLvo2SP7i(w?nbCk?^gTx9sgC!Pn22DKV3?7^Qz^ct-^ITU0d^BJ`9O8Rq9%rAf3FG@`<^X46~8a z@+|Lmq`eZIlrpu*#3F4At9<;MEW3NvW7v(9;hB0ePNY1e*I5xiO3N=qde#3t%Cc0& z!YQMhw52PQW>~J@L=WOg@Kor8x13cvX%;MmO$c{kM42IMncT+mRC!wKBUkKtRED+7 zTszg3ZPimm`bcYH=qV8PVPtH=dzs&PXGK>&8~eyR#yBo=XJVu8RFYfCTSdz;6^1ob zF^&m_o&N|AuvW!7HW=mxs?R$QGIk7&Kk4)d3@xX-vN;`JTpv2UK2CGQiEWQmN&=mH zas1?0sK3>r9hCOvCbm$bf5ty0FfW>HKoKX;$d5PPz+Q`Tg{&`w8YanB`wa*&MbRY1 s)0FYU`vn+Ksg)Y<7`qYx15Pi;PNds*I1PWY02&F};FBL*6LaL%r4jYOZlpZ)B@74`&z4pp>QdNlm z1Scd8`~ZFwV!W+rQ7&1@GoI&pGjGTK{paT|0NZ%fLKW*xJZRz}L*tb)D!gW>_BO^0 zwZ1))3@eE;^4&B~rTZXKO-LuPnb2e5RE#gdTKKFy!)Br^FVxHip?N_oIFX%7#}B2> zY%crO1ZTCDZiivXi}SZqYs>Jgmz;>1;98go9|fn(WM^X@Vrb^ldod|x%rO|&52hxl zT#l7jByQIjQWBIkq-{H~Zo+*Liyu3Y%QNdv`I$_4s>~4=|8n`B3`0>AQ3OMMCiIm0 zG0yPh|MKreEEA+#pie4=@%&h1!MgM7&Bn1j_31qQayUr(`@_Mf;b1h{-5Y#mXpQXD zW%7-RgYV7_aVw61;pM`Ab|8JYb_Z?LP-o~ZdT9)+S3}=VPh=MG7!`U&@vDGd4eDeY z6mjy5e0}o=*!S|dNw!gfO)SyAeUX40Xi>zlOh~mPzo4iRInTdEi!0`G$?RU7UNU3G oP23{R3S~#XN^9*Sm`4Y<32R3!cOvL getEnv() { + Map env = new HashMap<>(); + + env.put("PORT", port); + env.put("MEMORY_LIMIT", memoryLimit); + env.put("CF_INSTANCE_INDEX", cfInstanceIndex); + env.put("CF_INSTANCE_ADDR", cfInstanceAddress); + + return env; + } +} \ No newline at end of file diff --git a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java index dd177c25e..0f36d20fd 100644 --- a/src/main/java/io/pivotal/pal/tracker/WelcomeController.java +++ b/src/main/java/io/pivotal/pal/tracker/WelcomeController.java @@ -1,4 +1,5 @@ package io.pivotal.pal.tracker; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -6,7 +7,7 @@ public class WelcomeController { @GetMapping("/") - public String sayHello() { - return "hello"; + public String sayHello(@Value("${WELCOME_MESSAGE}") String message) { + return message; } } \ No newline at end of file diff --git a/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java b/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java index fda0f0f34..f8e13e679 100644 --- a/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java +++ b/src/test/java/test/pivotal/pal/tracker/EnvControllerTest.java @@ -1,21 +1,16 @@ package test.pivotal.pal.tracker; +import io.pivotal.pal.tracker.EnvController; 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" - ); + EnvController controller = new EnvController("8675","12G","34","123.sesame.street"); Map env = controller.getEnv(); diff --git a/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java b/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java index bfa8271a0..bbdf2d870 100644 --- a/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java +++ b/src/test/java/test/pivotal/pal/tracker/WelcomeControllerTest.java @@ -9,8 +9,7 @@ public class WelcomeControllerTest { @Test public void itSaysHello() throws Exception { - WelcomeController controller = new WelcomeController("A welcome message"); - - assertThat(controller.sayHello()).isEqualTo("A welcome message"); + WelcomeController controller = new WelcomeController(); + assertThat(controller.sayHello("A welcome message")).isEqualTo("A welcome message"); } } From 0890ac283b4b8385fd393574a1d15e805f99fdcd Mon Sep 17 00:00:00 2001 From: Mike Gehard Date: Wed, 3 Jan 2018 16:17:48 -0700 Subject: [PATCH 05/16] Add deployment pipeline --- ci/build.yml | 24 ++++++++++++++ ci/pipeline.yml | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 ci/build.yml create mode 100644 ci/pipeline.yml diff --git a/ci/build.yml b/ci/build.yml new file mode 100644 index 000000000..4ba28db53 --- /dev/null +++ b/ci/build.yml @@ -0,0 +1,24 @@ +platform: linux + +image_resource: + type: docker-image + source: + repository: openjdk + tag: '8-jdk' + +inputs: + - name: pal-tracker + - name: version + +outputs: + - name: build-output + +run: + path: bash + args: + - -exc + - | + cd pal-tracker + chmod +x gradlew + ./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 new file mode 100644 index 000000000..c34c3b2a9 --- /dev/null +++ b/ci/pipeline.yml @@ -0,0 +1,83 @@ +--- +resources: +- name: pal-tracker + type: git + source: + uri: {{github-repository}} + branch: master + private_key: {{github-private-key}} + +- 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: production + +jobs: +- name: build + plan: + - get: pal-tracker + trigger: true + - get: version + params: {bump: patch} + - task: build and test + file: pal-tracker/ci/build.yml + - 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-production.yml + path: pal-tracker-artifacts/pal-tracker-*.jar + environment_variables: + WELCOME_MESSAGE: "Hello from the production environment" \ No newline at end of file From 2bc426c2ce4044f33738dfb6bd19b695d0cd15bc Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Tue, 13 Mar 2018 18:57:31 -0700 Subject: [PATCH 06/16] Fly concourse --- ci/build.yml | 2 +- manifest-production.yml | 5 +++++ manifest-review.yml | 5 +++++ manifest.yml | 4 +--- 4 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 manifest-production.yml create mode 100644 manifest-review.yml diff --git a/ci/build.yml b/ci/build.yml index 4ba28db53..e2bb9473a 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -21,4 +21,4 @@ run: cd pal-tracker chmod +x gradlew ./gradlew -P version=$(cat ../version/number) build - cp build/libs/pal-tracker-*.jar ../build-output + cp build/libs/pal-tracker-*.jar ../build-output \ No newline at end of file diff --git a/manifest-production.yml b/manifest-production.yml new file mode 100644 index 000000000..340b53421 --- /dev/null +++ b/manifest-production.yml @@ -0,0 +1,5 @@ +--- +applications: +- name: pal-tracker + path: build/libs/pal-tracker.jar + 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/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 d5dea7fcd4a3343ffbc31fee225cc5b6223ed417 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Thu, 20 Jul 2017 15:04:20 -0600 Subject: [PATCH 07/16] Add tests for MVC lab --- .../InMemoryTimeEntryRepositoryTest.java | 71 ++++++++++ .../pal/tracker/TimeEntryControllerTest.java | 116 ++++++++++++++++ .../pal/trackerapi/TimeEntryApiTest.java | 126 ++++++++++++++++++ 3 files changed, 313 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..d0ae6cbe6 --- /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(123L, 456L, LocalDate.parse("2017-01-08"), 8)); + + TimeEntry expected = new TimeEntry(1L, 123L, 456L, 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(123L, 456L, LocalDate.parse("2017-01-08"), 8)); + + TimeEntry expected = new TimeEntry(1L, 123L, 456L, 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(123L, 456L, LocalDate.parse("2017-01-08"), 8)); + repo.create(new TimeEntry(789L, 654L, LocalDate.parse("2017-01-07"), 4)); + + List expected = asList( + new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8), + new TimeEntry(2L, 789L, 654L, 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(123L, 456L, LocalDate.parse("2017-01-08"), 8)); + + TimeEntry updatedEntry = repo.update( + created.getId(), + new TimeEntry(321L, 654L, LocalDate.parse("2017-01-09"), 5)); + + TimeEntry expected = new TimeEntry(created.getId(), 321L, 654L, 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(123L, 456L, 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..d80f2b999 --- /dev/null +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -0,0 +1,116 @@ +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 timeEntryToCreate = new TimeEntry(123L, 456L, LocalDate.parse("2017-01-08"), 8); + TimeEntry expectedResult = new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8); + doReturn(expectedResult) + .when(timeEntryRepository) + .create(any(TimeEntry.class)); + + + ResponseEntity response = controller.create(timeEntryToCreate); + + + verify(timeEntryRepository).create(timeEntryToCreate); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED); + assertThat(response.getBody()).isEqualTo(expectedResult); + } + + @Test + public void testRead() throws Exception { + TimeEntry expected = new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8); + doReturn(expected) + .when(timeEntryRepository) + .find(1L); + + ResponseEntity response = controller.read(1L); + + verify(timeEntryRepository).find(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(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8), + new TimeEntry(2L, 789L, 321L, LocalDate.parse("2017-01-07"), 4) + ); + doReturn(expected).when(timeEntryRepository).list(); + + ResponseEntity> response = controller.list(); + + verify(timeEntryRepository).list(); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isEqualTo(expected); + } + + @Test + public void testUpdate() throws Exception { + TimeEntry expected = new TimeEntry(1L, 987L, 654L, LocalDate.parse("2017-01-07"), 4); + doReturn(expected) + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); + + ResponseEntity response = controller.update(1L, expected); + + verify(timeEntryRepository).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..91e271b45 --- /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(123L, 456L, 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(2L, 3L, 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 f22355313d9a2ae4fa22e5eabef45175bb83a7f3 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Thu, 15 Mar 2018 14:20:00 -0700 Subject: [PATCH 08/16] mvc pal-tracker --- .../tracker/InMemoryTimeEntryRepository.class | Bin 0 -> 2085 bytes .../io/pivotal/pal/tracker/TimeEntry.class | Bin 0 -> 2420 bytes .../pal/tracker/TimeEntryController.class | Bin 0 -> 3527 bytes .../pal/tracker/TimeEntryRepository.class | Bin 0 -> 541 bytes .../pal/tracker/EnvControllerTest.class | Bin 0 -> 1469 bytes .../InMemoryTimeEntryRepositoryTest.class | Bin 0 -> 3291 bytes .../pal/tracker/TimeEntryControllerTest.class | Bin 0 -> 5216 bytes .../pal/tracker/WelcomeControllerTest.class | Bin 0 -> 1018 bytes .../pal/trackerapi/TimeEntryApiTest.class | Bin 0 -> 6664 bytes .../pal/trackerapi/WelcomeApiTest.class | Bin 0 -> 1704 bytes .../tracker/InMemoryTimeEntryRepository.java | 53 +++++++++++ .../io/pivotal/pal/tracker/TimeEntry.java | 90 ++++++++++++++++++ .../pal/tracker/TimeEntryController.java | 63 ++++++++++++ .../pal/tracker/TimeEntryRepository.java | 16 ++++ .../InMemoryTimeEntryRepositoryTest.java | 15 ++- .../pal/tracker/TimeEntryControllerTest.java | 2 +- .../pal/trackerapi/TimeEntryApiTest.java | 2 +- 17 files changed, 235 insertions(+), 6 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/EnvControllerTest.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/tracker/WelcomeControllerTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/TimeEntryApiTest.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/WelcomeApiTest.class 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/TimeEntryController.java create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java 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..22f7cd95de5b7d6278f0411351c4a0b3fd6fbb0b GIT binary patch literal 2085 zcma)7U02*x6x}xyCS?dSe6_at0WH$bhjg^XS{+1NEEGBvEfl2s&@iNMYe;5gGOUH~ z{t17B4|OdqV%Ku{?2mG}ZxWKhLg8U+JCIJ5hMGY4vfOg5pzYfNGe=LSqOD`OTh+SfZq@42 zt2X7X7g$cU$zR}GjUQ~kx^l=cO^b2~3_uHSaFkJD$InP(HGoUP}fvDe8uATLH0Wizfjj7i*={ zT_{hYp}>{1T*z+Pj;%t_iwi1!O>PQ^1@50om#^bAp9QCPIiR?#Q7lZh-P{aQn=3Ay ze*a7qx+7Zw>rgCp)V)p{mAv(uaq4tdRsC}f&kZc;vWQB7Cjz-;ukAPNS*eOX`i25t zSG{TC5{3o((>{%x*Ti{TFfeao0S`>nF>YcJ4-G7t_!{4sc!XsWD|l>T6>BEG#beT) z*0CmvC|NH&wlFKik`Hx$?_?snpgp2*K*qS(le&08t>>a%av)i8P@G$ z$4L}x?R87%_1K9c=W`x+8P?TfFpj;^bJ!sd>^*_=$6aIBWSAGx!|>Tl7mxJgjORGi zQ+h5_xD+FVAGMB?(kd?V^Ee2BA^xEHhtIe8rr{zCzO&W1zD>(rWI~_5@;*fQ6}0tz zWPXSKYv}zB*E~G{6FR@L1%8*`4R;E+5AR`wPqrHwBfO{d)@XabQvMD40lGL2Pk(xe ztlCyy!uSK{DhKG!V6F0kei~AaK_U*}3gcBA7b2XPgNQsCBa7hMc+56OHWgh#p^p&y z067h7Y4GaL1xz;2WK+;W0$RXTTnj-t4l(F<3_6yA_GtK-sk}%nriQ=Kt1`%RWH7=E zMydVf4zjU?Y%C@#hquH4`6~DzW>v!JD%OdSSS^*$L?l(2w*3F)x`r+e@et!ynT>`* z3@2p6F`3F<38%_Df$P*mmAw}3f&N!0tgEaJ&?m5u{*)xk3*d#c-u*Fn)JavIe~6E` i9`=!{L_U>>qWwZkRN)BiW`{2MB-DITgd3s%&Hn(#*VPLE literal 0 HcmV?d00001 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..c8ec90eee3566e8c33a65db87cb8069572917ed7 GIT binary patch literal 2420 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 LfbI;3p)>pjct)A& literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/EnvControllerTest.class b/out/test/classes/test/pivotal/pal/tracker/EnvControllerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..1b42e8d2ca00f5017f3fa42175a11ea1d67916d3 GIT binary patch literal 1469 zcmbVMTT|0O6#lj?B!nU*ToiABB2qvicaV$LwkTr@3Y{uXOo%I_mZT(GMt_zs;AO_~ z*$01;PmnsF6I zEAC=K!K8*MOlz3Itcp1m^C}h;+*7c~(7a?jwpd|^4kwEYv9$Y=GqmSyhp$%;cDYwD zcguw6IoC4FMboq8xems}zU?!Nw{e@>*ueoProujnt2+u8-iHf}B zlY3kc&Y)SRR}4cr+x<5Mg-5xJZO@g=Bh#n~yKLmm3gJCx6~3+fR5^3jbtyQOO=r*8 z5}xhsEjA)6M5txoSiB-^*YOoBG4yU$9bq5vqV3yMgOuY?y0vSDM9%g0j8c_qY1Gry zw%n>`@l{)vucx69<5GVdo#ocY{I4*-)8hg62Sx|64jt5AQ##+Tfc`JArM;aLg**I1~r15+f3?t`n{}~k{|A)xI?ifx6 z!J&z1J2OI5SQ<U**}IbwAg<3uxgeIF2h1t#c6hb zrL4b<0d&v@NB$z9rlE^$H=PVx8LdqtCt&Yt*F9vLYoLaUw2uc4xP)FhF&fVrp+XH|fnMP;#TkQ1fGY%Lix?d_n7b#P#Sg+MbW;7s6O9z}mxDQ-CGH zShK{Wx+tW&>rlwHA%S+%(SanTwoFMqBW0Wrsk%QEWDN#sWw=E)f}tOln0w^_ literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.class b/out/test/classes/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.class new file mode 100644 index 0000000000000000000000000000000000000000..9896a4a592050505e1ce26dc9caef161b48d3f03 GIT binary patch literal 3291 zcmbW3>sAw27>3_T1}2U}MWk9hRG=OLh=YJ~P_aRQLKCcjq-a|m!U%(5CQK$Owbiy4 z(3|KL^iO2f)%I^!|Mh?Gp?A>Lch3xysKiLK7R=7KzkPV#@7+7|=fA)F24E0Bs5pa3 z1yd?w;`vDf!{YNaY)F43Fs)(+pQ@0N7HJvLnpJTVpQ*ToIq`K~bS#8p;_ruz*pCU^ z-kC9`Vi8%9_qiCmq~Z?l?yQnZ;0w|9rTF)i=)Wgg_f;5JR*+M$BBAk$Wn12ugm|)R zQG$HkSurIXNL#i!TiRGQ-34Pg&qzz!$r<@Y!?ncsFerO#R#C!e+B1uuUa%fHo{`rJ z{PSER_t13pX**+XIPTVhwP8-!p1UgcG)FiFl{+|or=>m z2h*%X3rXm#HB*$saS8KD!OVFkrP#-ftoWOhpiVr=nFY^s?4km0u5GSldt_L&iWd1( zwoO#On@gu1cU506QAa(5k{?($XO#0+(Gv;l#v?;7c~)LeGs0yLzbfZeVMo%z*K)7a zhZqwomI^Drib|-11T6^d5H+rtc~j^dv$!*9hK%!0$<3LQmZTu?Ru$ z!kR&8u2o|OdJ%Bl{8xmBFlEE)L)-Zdk8UpZQ{Vb%1ed6IZ< z@+_TB&b*UyfiwJ@AmzHo7PV+JisDibbKI?}cO=9ccSlPyEPwTdG7zFvVl^ZbYef*d zSE`aC;p*Ncstjjorjj%D9raGq$&{ET){}Q?kflO%j5|)=H0%g6k#lU%u!Qf#9Csvi zztu0Vj7JAf^XFWA8X<}MkN>;)P3KeMv!VM1q@US{p*u_hn$W|x79`*tdihP_EtW*w zAN)I1h@a`(FA-aMfq1&-CFG@6G_*o#*|!a~Iq?Gf%g{0+uD;gMG<$9v@AZ~(Alq}U zjOH>9j>>KFPOP$-520|8W+ZTs9EUK3R(y=ZeDgSh1$^LdaxmCPxXA=EBEJ9+gBYfq*i6e zB1!(H3xs*ydXi*(cLszfge)g1OJMBwSrHcIZAEog)2J*;-X>R@I|@`x5IB2)DhyJE zODyRy--JhSpTGE^1;dy$3`=BVxLRK))mDhlNe zZ=C9EEw2bc#=|q+5#L5}-YZd(l=ZRtBKV*FT=>Px# literal 0 HcmV?d00001 diff --git a/out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class b/out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..c64493381945a7b329f3f018372b3e7c5c5e6b3c GIT binary patch literal 5216 zcmb7IX?qjr6+I)_9(foD7~_=S1VRE9UJzSY3L!PdPH<$owS^r*TW2hf?J=@O9*qDe zE$I^4(k0#ZeczX)V4KjSbWhW?P1BG42mKL!dhX0 zWHF<`L2eyFaK%VoCN65Ygv%Oc@pL_&!Fx2k7guELSs8nuhWF!&?D&A}_@FF#PA*pq zjzhsscuN66X9OS8@L_yJwq28LA64-&vG#EdpTP5l4gU#lLeC4VPs-{~$?8uFXhZHk zBV(Txcb`-7c@AJGpnFIZrQP{bbS%sNu4AMwm`*HN zx%#*QEoIY!olcuhHG8{?tGT!Cgq5+}lM2GoJwpm=y6iDi!Ci4HV&*K+S%x> z^&tk4Mg-u1X^hcm!Ha^X#e~jg3#9f{>+Qtaah)WhO$GNO3ES@b^Ol@8)266F8?^II z%IvX3!Og|uS**pq0!GK%(5axc`npr`B^_VJS9F}f!&U4oITc@D)o~J!DA=)2GW;_g zKgTar{8GoS@PdjLb^IFFRou`qhj|?fxG6I)sko)%H+WgcEBLLB+qkadceq}T;fRXg z>-YoysN+xgvt0hd;#r(evGuBozl!zObo>pk>-anVA(wyZctcFTfq&_EQ%vuZG~P+? zpkzIFX(KZc>mQwB)v}M4hKl`!qf)oicIpEAYOK%yW{;B*1-qiY%so3@X(N|AzNc)& zw@lH+qHES!sdpcjChCKcP89Qs{IsXn+N|M7H=(@}*se*#88m0|W+r78+%Mip1lyv$ zz5bphQ)k3mGiO(sj*Qs@rki&%?9=6*gZTX3R^TY-=0``_)!+T+ZRrd6Ic) z(xmC#%bG65S!A=Z++3gGrY22?M@ZPn@Z76Q1#P@%ZO+NoTD3#;eN`E%zP;C7G#zW4 zP+O|pqhS4n=??M;%jf*FwI#aBdGZ(!6@M|oZxRBgGi|VaT1v5Pmc8TpG`1He%JKQ`A)|%W+|UK zBj-N^?24YRq$Oag{K=pu?L9Lz6j&^uYg@jLh@JMFzDcqZ|9$7qCh2+(bi& z&o!Zj$O1OsM&u?MLm1|a+BSzR30bm$CN606XFBF^_X6JXBDT)qo?-slwt(jLIovy3 zim8=&K(e=BC))4;4&p(Qy^9gFU;wRrDQ&|H+W8{d;l_@E4VS8UG;$Vis`4R7si1|w* zCOLk@VTdwzQ$WCeG~od5W4tYVx$nSXevVQZ$8eUyGblY#&JG`45N#9UNP5v84q^;( zwXZLKWQg&v6!ma1>T9$b;=Y)&dVFyr_rtkg!jYu;A>2c0fwd4}9c-f6yBXLH0^UoY z0=vmKAHd#$yS#)bMX>(J5Yx62*e42Ll`z(OBwVe1nc@sDo2bEaHNKp8HsZlX>~4rs z(ds-l4leNhb7>ZDqE+#eIES{f9QxyxDZC99KnJiJw(MGqZa%5 z8K(LssKY!p`5ZG=%H-96l4D*b&yj#(k|t{PGbzORb59oM&U0)fn%-HU39koA&IAO- z8nM4fLj?ue0uGj{_)txtX~y_a86g_$Smr7zDOYunV`aD#@Bs;Y>K2qao{;?fOP>ypM@> zeK{6!-1g@{POQ?Aaqxg)=Uoz6HI_YP6zOb65lNbc88&b4Jv_v|gGU}}s5@wQIKX3u!>jd<%18zA zP(%a%wm*`AW!Rj%F8aYxG~#U5;Qg9EU(&I$iUvpZe_af_X^Vt0GPWZg=vZ&R~8fB!UB37|Q)RcQO%bjtHL@#5L_}3{`AbW#W OWJ@s96(@BQCQ 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 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 new file mode 100644 index 000000000..492d5e5e5 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.java @@ -0,0 +1,53 @@ +package io.pivotal.pal.tracker; + +import org.springframework.http.ResponseEntity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InMemoryTimeEntryRepository implements TimeEntryRepository{ + + public InMemoryTimeEntryRepository() { + } + + Map timeEntryHashMap = new HashMap(); + + @Override + public TimeEntry create(TimeEntry timeEntry) { + timeEntry.setId(timeEntryHashMap.size()+1); + timeEntryHashMap.put(timeEntry.getId(),timeEntry); + return timeEntry; + } + + @Override + public void delete(long id) { + timeEntryHashMap.remove(id); + } + + @Override + public TimeEntry find(long id) { + if(!timeEntryHashMap.containsKey(id)) + return null; + return timeEntryHashMap.get(id); + } + + + @Override + public TimeEntry update(long id, TimeEntry timeEntry) { + timeEntry.setId(id); + timeEntryHashMap.put(id, timeEntry); + return find(id); + } + + @Override + public List list() { + return new ArrayList(timeEntryHashMap.values()); + } + + @Override + public boolean contains(long id) { + return timeEntryHashMap.containsKey(id); + } +} 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..6a4412ca4 --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntry.java @@ -0,0 +1,90 @@ +package io.pivotal.pal.tracker; + +import javax.annotation.Generated; +import java.time.LocalDate; +import java.util.Objects; + +public class TimeEntry { + + private long id; + private long projectId; + private long userId; + private LocalDate date; + private int hours; + + public TimeEntry() { + } + + 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 TimeEntry(long projectId, long userId, LocalDate date, int hours) { + this.projectId = projectId; + this.userId = userId; + this.date = date; + this.hours = hours; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getProjectId() { + return projectId; + } + + public void setProjectId(long projectId) { + this.projectId = projectId; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public int getHours() { + return hours; + } + + public void setHours(int hours) { + this.hours = hours; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TimeEntry)) return false; + TimeEntry timeEntry = (TimeEntry) o; + return getId() == timeEntry.getId() && + getProjectId() == timeEntry.getProjectId() && + getUserId() == timeEntry.getUserId() && + getHours() == timeEntry.getHours() && + Objects.equals(getDate(), timeEntry.getDate()); + } + + @Override + public int hashCode() { + + return Objects.hash(getId(), getProjectId(), getUserId(), getDate(), getHours()); + } +} 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..dbceb22fd --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -0,0 +1,63 @@ +package io.pivotal.pal.tracker; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +@RestController +public class TimeEntryController { + private TimeEntryRepository timeEntryRepository; + private ResponseEntity responseEntity; + + public TimeEntryController() { + } + + public TimeEntryController(TimeEntryRepository timeEntriesRepo) { + this.timeEntryRepository = timeEntriesRepo; + } + + @PostMapping(value = "time-entries") + public ResponseEntity create(@RequestBody TimeEntry timeEntry) { + TimeEntry timeEntry1 = timeEntryRepository.create(timeEntry); + /*HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentType(MediaType.APPLICATION_JSON); + ResponseEntity responseEntity = new ResponseEntity<>(timeEntry1,responseHeaders, HttpStatus.CREATED);*/ + + return new ResponseEntity<>(timeEntry1, HttpStatus.CREATED); + } + + @GetMapping(value = "/time-entries/{id}") + public ResponseEntity read(@PathVariable long id) { + if(timeEntryRepository.contains(id)){ + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + TimeEntry timeEntry = timeEntryRepository.find(id); + return new ResponseEntity<>(timeEntry,HttpStatus.OK); + } + + @GetMapping + public ResponseEntity> list() { + List timeEntryList = timeEntryRepository.list(); + return new ResponseEntity>(timeEntryList, HttpStatus.OK); + } + + @PutMapping(value = "/time-entries/{id}") + public ResponseEntity update(@PathVariable long id, @RequestBody TimeEntry expected) { + TimeEntry timeEntry = timeEntryRepository.update(id,expected); + if(timeEntry == null) + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + //TimeEntry timeEntry = timeEntryRepository.update(id,expected); + return new ResponseEntity<>(timeEntry,HttpStatus.OK); + } + + @DeleteMapping("/time-entries/{id}") + public ResponseEntity delete(@PathVariable long id) { + timeEntryRepository.delete(id); + if(timeEntryRepository.contains(id)==false) + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + return new ResponseEntity<>(timeEntryRepository.find(id),HttpStatus.OK); + } +} 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..ddbfe336d --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryRepository.java @@ -0,0 +1,16 @@ +package io.pivotal.pal.tracker; + +import org.springframework.http.ResponseEntity; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public interface TimeEntryRepository { + public TimeEntry create(TimeEntry timeEntry); + public void delete(long id); + public TimeEntry find(long id); + public TimeEntry update(long id, TimeEntry timeEntry); + public List list(); + public boolean contains(long id); +} diff --git a/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java index d0ae6cbe6..51d590525 100644 --- a/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java +++ b/src/test/java/test/pivotal/pal/tracker/InMemoryTimeEntryRepositoryTest.java @@ -51,9 +51,7 @@ public void update() throws Exception { InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); TimeEntry created = repo.create(new TimeEntry(123L, 456L, LocalDate.parse("2017-01-08"), 8)); - TimeEntry updatedEntry = repo.update( - created.getId(), - new TimeEntry(321L, 654L, LocalDate.parse("2017-01-09"), 5)); + TimeEntry updatedEntry = repo.update(created.getId(), new TimeEntry(321L, 654L, LocalDate.parse("2017-01-09"), 5)); TimeEntry expected = new TimeEntry(created.getId(), 321L, 654L, LocalDate.parse("2017-01-09"), 5); assertThat(updatedEntry).isEqualTo(expected); @@ -64,8 +62,17 @@ public void update() throws Exception { public void delete() throws Exception { InMemoryTimeEntryRepository repo = new InMemoryTimeEntryRepository(); TimeEntry created = repo.create(new TimeEntry(123L, 456L, LocalDate.parse("2017-01-08"), 8)); + repo.create(new TimeEntry(123L, 456L, LocalDate.parse("2017-01-08"), 8)); + repo.create(new TimeEntry(789L, 654L, LocalDate.parse("2017-01-07"), 4)); + + long id = created.getId(); + //System.out.println(repo.list().size() + " = before delete size"); repo.delete(created.getId()); - assertThat(repo.list()).isEmpty(); + //System.out.println(repo.list().size() + " = size"); + + assertThat(false).isEqualTo(repo.contains(id)); + //for deleteAll + // 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 index d80f2b999..c6c4d2a7a 100644 --- a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java +++ b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java @@ -21,6 +21,7 @@ public class TimeEntryControllerTest { private TimeEntryRepository timeEntryRepository; private TimeEntryController controller; + //Execute before each test method. @Before public void setUp() throws Exception { timeEntryRepository = mock(TimeEntryRepository.class); @@ -35,7 +36,6 @@ public void testCreate() throws Exception { .when(timeEntryRepository) .create(any(TimeEntry.class)); - ResponseEntity response = controller.create(timeEntryToCreate); diff --git a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java index 91e271b45..ea11dd73e 100644 --- a/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java +++ b/src/test/java/test/pivotal/pal/trackerapi/TimeEntryApiTest.java @@ -38,7 +38,7 @@ public void testCreate() throws Exception { assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); DocumentContext createJson = parse(createResponse.getBody()); - assertThat(createJson.read("$.id", Long.class)).isGreaterThan(0); + assertThat(createJson.read("$.gbb b", 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"); From 16a19531f5c6fac45e6e092763bcc593d794327b Mon Sep 17 00:00:00 2001 From: Mike Gehard Date: Tue, 9 Jan 2018 09:47:21 -0700 Subject: [PATCH 09/16] Add task for migrating databases --- ci/migrateDatabase.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 ci/migrateDatabase.yml diff --git a/ci/migrateDatabase.yml b/ci/migrateDatabase.yml new file mode 100644 index 000000000..b2af8c5ef --- /dev/null +++ b/ci/migrateDatabase.yml @@ -0,0 +1,29 @@ +platform: linux + +image_resource: + type: docker-image + source: + repository: openjdk + tag: '8-jdk' + +inputs: + - name: pal-tracker +# - name: version + +#outputs: +# - name: build-output + +run: + path: bash + args: + - -exc + - | + cd pal-tracker + curl -L "https://packages.cloudfoundry.org/stable?release=linux64-binary&source=github" | tar -zx + chmod +x cf + curl -L "https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/5.0.5/flyway-commandline-5.0.5-linux-x64.tar.gz" | tar -zx + chmod +x flyway-5.0.5/flyway + ./cf login -a $CF_API_URL -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE + ./cf ssh -N -L 63306:$MYSQL_IP:3306 pal-tracker & + sleep 2 + ./flyway-5.0.5/flyway -url="jdbc:mysql://127.0.0.1:63306/$DATABASE_NAME" -locations=filesystem:databases/tracker -user=$DATABASE_USERNAME -password=$DATABASE_PASSWORD clean migrate From 51dd7f6c7b0085d6f8fc9c71415a2289d316e0f2 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Fri, 16 Mar 2018 08:21:11 -0700 Subject: [PATCH 10/16] database migration --- databases/tracker/create_databases.sql | 10 ++++++++++ databases/tracker/migrations/V1__initial_schema.sql | 11 +++++++++++ 2 files changed, 21 insertions(+) create mode 100644 databases/tracker/create_databases.sql create mode 100644 databases/tracker/migrations/V1__initial_schema.sql 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..daca8c4e3 --- /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; \ No newline at end of file From 75700e52f07b1af6692f6fe448a8ad67575afe31 Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 11:45:04 -0600 Subject: [PATCH 11/16] Add tests for JDBC lab --- .../tracker/JdbcTimeEntryRepositoryTest.java | 159 ++++++++++++++++++ 1 file changed, 159 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); + } +} From 0bf155a88efce9f29694cfa8a4608f0529fc6e02 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Fri, 16 Mar 2018 21:53:26 -0700 Subject: [PATCH 12/16] JdbcTemplet --- build.gradle | 26 +++++- ci/build.yml | 13 ++- manifest-production.yml | 6 +- manifest.yml | 6 +- .../pal/tracker/JdbcTimeEntryRepository.class | Bin 0 -> 5933 bytes .../tracker/JdbcTimeEntryRepositoryTest.class | Bin 0 -> 6412 bytes .../pal/trackerapi/TimeEntryApiTest.class | Bin 6664 -> 7430 bytes .../pal/tracker/JdbcTimeEntryRepository.java | 88 ++++++++++++++++++ .../tracker/JdbcTimeEntryRepositoryTest.java | 36 ++++--- .../pal/trackerapi/TimeEntryApiTest.java | 19 +++- 10 files changed, 173 insertions(+), 21 deletions(-) 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 8009ec0fa..55cb42a6e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,9 @@ +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 { @@ -9,12 +12,33 @@ repositories { dependencies { compile("org.springframework.boot:spring-boot-starter-web") + 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") + testCompile("org.springframework.boot:spring-boot-starter-test") } + +def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?useSSL=false&user=root&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" bootRun.environment([ "WELCOME_MESSAGE": "hello", + "SPRING_DATASOURCE_URL": developmentDbUrl, ]) +def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?useSSL=false&user=root&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 +} \ No newline at end of file diff --git a/ci/build.yml b/ci/build.yml index e2bb9473a..2455d052b 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -18,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/tracker/create_databases.sql chmod +x gradlew - ./gradlew -P version=$(cat ../version/number) build + ./gradlew -P version=$(cat ../version/number) 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/manifest-production.yml b/manifest-production.yml index 340b53421..3ae5ad9e4 100644 --- a/manifest-production.yml +++ b/manifest-production.yml @@ -2,4 +2,8 @@ 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 + env: + SECURITY_FORCE_HTTPS: true \ No newline at end of file diff --git a/manifest.yml b/manifest.yml index 9b7fd344e..d849c8f83 100644 --- a/manifest.yml +++ b/manifest.yml @@ -2,4 +2,8 @@ applications: - name: pal-tracker path: build/libs/pal-tracker.jar - random-route: true \ No newline at end of file + host: ps-pal-tracker-review + services: + - tracker-database + env: + SECURITY_FORCE_HTTPS: true 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..7af75955a98734d4d4a6ae4d4beb81bc26a49811 GIT binary patch literal 5933 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 Lg7e8c4;TCg=GT5< 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 6263e1e4f0154355b94d3f41c3d049f5034d7f64..f71cb65a53f7c044e4a513794e508765f69c2825 100644 GIT binary patch literal 7430 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 rr(rJU-K)9(nmEt$>-g|`jtg-Y`PeK delta 2714 zcma);d0bRg6vuye7T%l3b9;gWxsVbmFbIf-guA#TxRoM`sVoU*pn!^Ly;il&mZ_{L z+pKI=!3LBRODnUoY_-j@#Y!!$Y|rX<=go|wV;|>_`|iEx-t*r5p5HllmSiqVH+THA zaWjB)5w4*%mMU20#&t5xH!YhDY(3sp4_jS*_v;6;DdTQyQK| zt%_#?AEIh}+q3c|YozP7D%L5e3r7#s%k}FuJO{6e2I;3(L8FGASgK-!d~TFYlWd+> zu_@ru7UZ^l&{$uFuSB_n|$d@vUypqcty@_mndG<5QQBIUaOxg za)f7!eRpduyQZjQPIXyDd6k0K>r)-EcCphlUa1KigRug!o_0!e3EB;b*z>h=O0F zt0SI6u0C%3ir;iLzw7t|e+oF_lFR5>OG`;T*PW8;PU)-QFCBm5s6aQ**!G=zWtUf% zRWFnf{?YNTfGz}%>cWOSy0Ejs7~#-`Q_h76MHi}Yd1|%c4ozSP9YlE6=$hwXSO-r` zxGDO|nff*QUZv*KDO+tmhD2~GT%<;{VHlGT|j0@Xwp5};Tb3Er{V1XDo&;=K; zr_%~_!iAW?r@%z6w__Z;UEyR8s7qLna6@OS8LjM_(AtYOIf-7hZA7~|7+$o`<0xVa zS~VgvH_7N=L~+#_8xZZl7DP27#^uGCc}a%Z@gz$G>|S)Dt#b&I(=v7Oy*1#EFNrl2 zFV5not|2yclbhD@v+a<_jfabEH$<>!c13r%nO4J)fEh@{Vk9xY+*pTXAB$M)ynYrD zScyDLA`>^RLp~;x+!Q*K#4K9@ds8{%;Pawj1g3FDV0wTf#o%CMfO0dT0cYn}Gy^2# zbCUuj&ygfK@{=6xHo6-$f>s$Q{oG2ryLY^m*1d2$*#+?IEOd59XaH9kl z^L#}J$4sts1_@c~3%EW=$UFg2X#kNJOcxHmd|QxE6ME)1AjL@aB8|uC+#OFR8@g*?ZfNWA3LHUg`k0pTuuV3 znQU9BRS7@Aq7a1N?1vvuDVF)*51I&i3aowM56y_cMI`y7KG1!IKjH8WSRxy1hF(GE~Uqm_mI)Z6n*$QZz3Nv)2s0gG1 zZ<#Qk080Na4eBsR1OsradJLA;;}o&;FE { + 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 void delete(long id) { + jdbcTemplate.update("DELETE FROM time_entries WHERE id = ?", id); + } + + @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 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 List list() { + return jdbcTemplate.query("SELECT id, project_id, user_id, date, hours FROM time_entries", mapper); + } + + @Override + public boolean contains(long id) { + return (find(id) != null)? true : false; + } + + 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/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java index e1eac20fc..4202b02f1 100644 --- a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java +++ b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java @@ -18,40 +18,46 @@ import static org.assertj.core.api.Assertions.assertThat; public class JdbcTimeEntryRepositoryTest { - private TimeEntryRepository subject; + private TimeEntryRepository timeEntryRepository; private JdbcTemplate jdbcTemplate; @Before public void setUp() throws Exception { MysqlDataSource dataSource = new MysqlDataSource(); - dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); - subject = new JdbcTimeEntryRepository(dataSource); + //this supose to work for the time being i use the below datasource + //dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + dataSource.setUrl("jdbc:mysql://localhost:3306/tracker_test?useSSL=false"); + dataSource.setUser("root"); + dataSource.setPassword("root"); + + timeEntryRepository = 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); + TimeEntry newTimeEntry = new TimeEntry(123, 321, LocalDate.parse("2017-01-10"), 8); + TimeEntry entry = timeEntryRepository.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(((Date)foundEntry.get("date")).toLocalDate()).isEqualTo(LocalDate.parse("2017-01-10")); 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); + TimeEntry entry = timeEntryRepository.create(newTimeEntry); assertThat(entry.getId()).isNotNull(); assertThat(entry.getProjectId()).isEqualTo(123); @@ -67,7 +73,7 @@ public void findFindsATimeEntry() throws Exception { "VALUES (999, 123, 321, '2017-01-09', 8)" ); - TimeEntry timeEntry = subject.find(999L); + TimeEntry timeEntry = timeEntryRepository.find(999L); assertThat(timeEntry.getId()).isEqualTo(999L); assertThat(timeEntry.getProjectId()).isEqualTo(123L); @@ -78,7 +84,7 @@ public void findFindsATimeEntry() throws Exception { @Test public void findReturnsNullWhenNotFound() throws Exception { - TimeEntry timeEntry = subject.find(999L); + TimeEntry timeEntry = timeEntryRepository.find(999L); assertThat(timeEntry).isNull(); } @@ -90,7 +96,7 @@ public void listFindsAllTimeEntries() throws Exception { "VALUES (999, 123, 321, '2017-01-09', 8), (888, 456, 678, '2017-01-08', 9)" ); - List timeEntries = subject.list(); + List timeEntries = timeEntryRepository.list(); assertThat(timeEntries.size()).isEqualTo(2); TimeEntry timeEntry = timeEntries.get(0); @@ -114,14 +120,14 @@ public void updateReturnsTheUpdatedRecord() throws Exception { "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 timeEntryUpdates = new TimeEntry(456, 987, LocalDate.parse("2017-01-09"), 10); - TimeEntry updatedTimeEntry = subject.update(1000L, timeEntryUpdates); + TimeEntry updatedTimeEntry = timeEntryRepository.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.getDate()).isEqualTo(LocalDate.parse("2017-01-09")); assertThat(updatedTimeEntry.getHours()).isEqualTo(10); } @@ -133,7 +139,7 @@ public void updateUpdatesTheRecord() throws Exception { TimeEntry updatedTimeEntry = new TimeEntry(456, 322, LocalDate.parse("2017-01-10"), 10); - TimeEntry timeEntry = subject.update(1000L, updatedTimeEntry); + TimeEntry timeEntry = timeEntryRepository.update(1000L, updatedTimeEntry); Map foundEntry = jdbcTemplate.queryForMap("Select * from time_entries where id = ?", timeEntry.getId()); @@ -151,7 +157,7 @@ public void deleteRemovesTheRecord() throws Exception { "VALUES (999, 123, 321, '2017-01-09', 8)" ); - subject.delete(999L); + timeEntryRepository.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 ea11dd73e..5a91b9fdf 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,10 +14,12 @@ 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; import java.util.Collection; +import java.util.TimeZone; import static com.jayway.jsonpath.JsonPath.parse; import static org.assertj.core.api.Assertions.assertThat; @@ -30,6 +34,17 @@ public class TimeEntryApiTest { private TimeEntry timeEntry = new TimeEntry(123L, 456L, 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"); + + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + } + @Test public void testCreate() throws Exception { ResponseEntity createResponse = restTemplate.postForEntity("/time-entries", timeEntry, String.class); @@ -38,7 +53,7 @@ public void testCreate() throws Exception { assertThat(createResponse.getStatusCode()).isEqualTo(HttpStatus.CREATED); DocumentContext createJson = parse(createResponse.getBody()); - assertThat(createJson.read("$.gbb b", Long.class)).isGreaterThan(0); + 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"); @@ -123,4 +138,4 @@ private Long createTimeEntry() { return response.getBody().getId(); } -} +} \ No newline at end of file From f5ba76d196c2302ac76d850f20d3777f8f76731a Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 12:28:32 -0600 Subject: [PATCH 13/16] 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 f8ebdb820683317b03822c0aae06279efef7bd18 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Sat, 17 Mar 2018 16:55:49 -0700 Subject: [PATCH 14/16] actuator implimentation --- build.gradle | 21 +++-- .../pal/tracker/TimeEntryController.class | Bin 3527 -> 4301 bytes .../tracker/TimeEntryHealthIndicator.class | Bin 0 -> 1394 bytes .../tracker/JdbcTimeEntryRepositoryTest.class | Bin 6412 -> 6547 bytes .../pal/tracker/TimeEntryControllerTest.class | Bin 5216 -> 5781 bytes .../pal/trackerapi/HealthApiTest.class | Bin 0 -> 2761 bytes .../pal/tracker/TimeEntryController.java | 76 ++++++++++-------- .../pal/tracker/TimeEntryHealthIndicator.java | 29 +++++++ .../tracker/JdbcTimeEntryRepositoryTest.java | 8 +- .../pal/tracker/TimeEntryControllerTest.java | 34 ++++---- 10 files changed, 108 insertions(+), 60 deletions(-) create mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryHealthIndicator.class create mode 100644 out/test/classes/test/pivotal/pal/trackerapi/HealthApiTest.class create mode 100644 src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java diff --git a/build.gradle b/build.gradle index 55cb42a6e..48a0485a0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,27 +14,34 @@ dependencies { compile("org.springframework.boot:spring-boot-starter-web") compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.1") compile("org.springframework.boot:spring-boot-starter-jdbc") + compile("org.springframework.boot:spring-boot-starter-actuator") compile("mysql:mysql-connector-java:6.0.6") testCompile("org.springframework.boot:spring-boot-starter-test") } -def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?useSSL=false&user=root&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" +springBoot { + buildInfo() +} + +def developmentDbUrl = "jdbc:mysql://localhost:3306/tracker_dev?user=root&password=root&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" bootRun.environment([ - "WELCOME_MESSAGE": "hello", - "SPRING_DATASOURCE_URL": developmentDbUrl, + "WELCOME_MESSAGE": "hello", + "SPRING_DATASOURCE_URL": developmentDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, ]) -def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?useSSL=false&user=root&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" +def testDbUrl = "jdbc:mysql://localhost:3306/tracker_test?user=root&password=root&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false" test.environment([ - "WELCOME_MESSAGE": "Hello from test", - "SPRING_DATASOURCE_URL": testDbUrl, + "WELCOME_MESSAGE": "Hello from test", + "SPRING_DATASOURCE_URL": testDbUrl, + "MANAGEMENT_SECURITY_ENABLED": false, ]) flyway { url = developmentDbUrl - user = "tracker" + user = "root" password = "" locations = ["filesystem:databases/tracker/migrations"] } diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class index 9d62d61b1e177cc668340b38ac1a16909a9d6962..ddf4b24e9fe9250357382fac565cc84d535d1d22 100644 GIT binary patch literal 4301 zcmcInTXz#x6#h<v>b4J876|-dHwmEvx4Fvn?%sTesBNvgC@CD;TzEtq?-vppi4|GXg)vH>2nu@CK*- zE3Wgjb50;MWM)WKThhqs6UC*JZp~_`EWgB(W?IY6X_g`1%O6AbqEYZe$B;?-%xqS- zBpqo>XUGJ`>(epNS<{(C+s>=gdLeJ-3Nkc?EqQKQ3)G6hzPe0Pq1{E2@xsPEW{icL zW*05uyYpY-8>|oL3?c2DX3jH<=8S?tqR;1YOfuV`p91|gxwxyR)Rd9SsG29MPMHOJ zT+8QKFWhtA`aP#KSwbwV=iGc+zf;r;_60K|Ne?lY8G(U#@|L!&saY+zpe9Y)-Av>H z2DfP6xPw5;jLpKLCr-kJaYtbO`3?CDL7#7$NA)U}8m#H5-fhj8nHa{uP4yxxE=@+Fe7{W%=;` zH0g^00snb1RjkU3#*Ch2wLR+us#U(jjz*<`n`g|TmDVpBvJ>y{x>cpC_R885IN>|z z*gNftI)l3zRj2$b=vq|SuzGa&gjBE}mV%e?vVuL>tKb#v3!|VQ0cQ0U4}5RA_l8ka zu#CH0liQTf*{DKOHY%sz4i13f^m6v*ELkRln7k(b+*+r&BQrgD;PemW;|+*9CKiac_XmB zy1r+4)shDYeTuQKIh1(qiY8mve__ik824Biq~S78N#B5~Z$E5rg<@*SAeN3AScWB- z{k3*>^6nDvBRs0?B2Ve?(Dcap*^yy^gEh->g980IRc+7>`+zT13CbnIJ9bHHmZ$HW zdlr$AG17CB*8{?dk9F2>)B=IYE8Jyba(4dW-Ummfu4hw%d`tO}P4qqmysk$+07xnM4q9W)ee?HWTQg zytMp?W02!u;%9`qN@xgRoYO$8v4n5{oCaeNxmH4xKncwPk4uP#@EeqC-K%JMiq;>g zAAm$Cu>rdfrT-md;8j|d=%iKJ@X!sS8$HAk#S!#!7J$n20OgNxl-wNU_!@sjR}AK2w!-{N2|BaBIea=wbKPq9sc4LV?5I`@;t0|b7M zfjUI;;#GkAJivW8juXVtN*M_~fRo%O0`V6*!#GujKj6S`rQ9HYr>{d4aHitMS;viY zI8OkQ`*FGz1;)t(Y*=>JVji3n>m7nMQKmcs<`T9lrDuAJPHRXA$S%Hms$Is zqYO{~8f)Dpbc8U`vqsws9)g~-!B`z7h0?bU*NEra>9TJw?Um49-hkZ6V2M0{G3R;3aWcmDB;Ut9@ri!`ai*>) literal 3527 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 delta 2561 zcmbVO`&U#|6#mXU@0=MBhl>QJKqBM}h$22RO)x4X@P&#)Mj?*92#OiAvKo6BS(cBR zX!d^E)3kxhj3nBN%BWWT6aLofFL1Tbx%Un;uwYgEaL(TQoV(BdzWo@qC5NhGm#>~5 z0Z_v@$6UnR*w0}@Ou^G)e@4NxVt-C72i$m`!-GB#w%`Rf4s!Sm6NXb9uj38j=}iT1dGR*h;dmGC zalDTY6nv=QBL%13_?Tk>J`qQsa(pI^KIixXXB2!XjAuE{;Va=VE~Eh=4T?n<%aFK| z;7H=Ug0f*VGW!)T`th}R>4Jh0pX&_gXDe3;Xedg=ej}II7@B z1wXm*bG(~1GIlwhv>mqDLuk`(cb2iG+6iZAAR3ND*X)UeTUwe{@9yY|gnjr0V+@Yo zo}Q==zhcaX--KbIcGR1%{pHE$K7{bQ4_mQKYj;)ITd`d`;woZe+F4gK3&pRxqRi38 za8N7pG{i@gPR3o4aCB>L7ek)*2UiQWF}Q^#67FU2lC-fS64~3+yDNTx&v3=IP{u)t zwTUOBL{HGp!RX~STJIpOigrf3GcW{pPF~wVt4kVH+)1*}xPW%tMH<81^s!xjd=d_F z57ZCC78-)RDd>0Ta0dM@or+CvN?;hCP;dx*5MGjfLe3gQc3=S6{uw%Q{4*2qCy+b- zco~%;PzVJ@$VM?`wS=^JC_^c#F&{Ug95+*qh~Q=!0hqxlScMMkq#!$Ebm$;q7s4b7 zJiBRUxW|f~mCyqYvYj4=8d8y`BHv$-Kw*wDgxO6LpEsDqwKg=%Q0F)p`$2) z;uakx>nr`&B{9#21WHY_j`^*X{xV_RF3jYWE*sWz>n^>MGF~r@m#{!bAc3HKUq=Nw zskE-=CI4=Ma2-p8RwK21Q(G!P z3MT?2oi0mYd5)rEg+yl~bm81`iLTgqJB4mz3f(Oxx~UL6oI-Ghe2DsNml?c)icMry z#mXdB+0hh~xujTEis(o)BVT5!X`$0$M(CtZ4!uq_LN{u%-3)ye8qg~fGgZKmRKT0e zfb%lrq)O7Udb~lhB{!|eqDEHf;D{xA3WJ|S&akiJvrpnc$7Hjy^Y>%j9Z5^>@^hv#n zMp@!Hdm3-Uttp+h39L1yvo^6?xCK#ki<)oD0;5}^Cd9ck@E?;US3$-1 zxSR!30orE)VvEgr7@rJigE1Hh&_q0eff&v4|2iCv*;t<%gK4vIL(2GXW+P9)moXgI zT~n1d9OEfMj!POu&il-q*H0RbhGn|p_=H6k{a@gcqor1ol`&dsHrXMqn~k50nf5C* w!No5I{XVu>W8Guow5EEnV0QC(AJ>|JS5SY83C>a_YFN6bDbXjXm2G(HAJA^o&;S4c diff --git a/out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class b/out/test/classes/test/pivotal/pal/tracker/TimeEntryControllerTest.class index c64493381945a7b329f3f018372b3e7c5c5e6b3c..4a0f6935bd334b1f2ff069fbcb1e72be1dd4a9ee 100644 GIT binary patch literal 5781 zcmbVQ`Ck<09e!pP7#PQ>h~zTSXjE9l!K+HZ0})Nk0xhgkn;0@I0}L)Z%j_&l)1+7I z6`S7ok>2-}RB??>YHN=)J<=<^(lc#;=^xQgpLb?7c8~at=$ZnIj%s{B!3XgnvG&7a*+*o2RG9pj zf{)`9g#|D9jmYBy+b6~BC&cVe3FPCV^=Z-f8L{1GWqeM?=OwIm%n73_?by?OM#jpT zj%80v*cvx2En`kuj-Jvo{O{O$^0Z-ViTShJBq&LX7OYgtu*+H78O-L|8Anx1rWx?^Y)hGUz_tkyYeDUGS4dT!JR zOzkP()X~sF8mekH)26duLbRdrgoKJtYnaeh#LcwPlbaYa?1Vm)Voz<{O6sW-x^0Tz zerJU8&_rx=Xi!L?K+z@GLC7bvogLQ;pRSiiOxS^W=-zY zkxnx_Zit- z%8^iQ8`%s&(P(2y?~XZ6M&oRE5`|?t)3meFo$EpLnN|yceTF_vqlH~0)X#S4bf)0c z?s9i)Uz*o`B3f10ez3=Kj!;Mfhv|LJHd2OAgVJy1?4)tT6bfD&r1WepZWCZs+=F8hV&zu@ z8DCWKC45;$2M$Tt^MB^DiY^?Hu)FN#;rvx`6nB?_x=2{?{ECWB9G0+tDYA6nj+X6v znp?Ivx9pYiRTW>u*M)^|h{rcod<##?cuK{$aY4pK6*Iyemyj2a%PO80-B(n62hYfO zR>gO5RmS&Jd>_|j{6NJI@go&K#&criCo-N_@l*Uv#?Mv!0xzieC4Qyi*SMzQH@FtY zvPZ^mRlJDbsrWtqARd2Y0?*czu=yt$FNt0Ltl}^DtBSwj@8a+fPFTs)JaM#c@69?ZdL(RW@KVjS)0xt< z*|x?|k7t>}f1+W@TrZw#Z6))toO7l8g<%!8iG#x(>$BPJNy3h>Ukk1StmC% z#3NYi7Y)Pa=4-<-n>)_nof$J|dUHwBMG*wis|%a!)}7>-Ve{CG>S^9Us*?WEb!6!n z(_2gBEYWwDWvEoaDSf0>O&PX1La1BHTq9xmsNwYU*v@6W`^1)pMb6N>d60UO34Wc} zgLz=s&bXH339soHQ|oX$#2tZkz#C1B>CCyK;TUvU6V#iypU_dI?F?nb1!9ol}r}bgT)23+k9O>B-EJMuF0I9N?5!hhS|*$?|cvW%mO|# zLvY9%X4vZJ{1U1>aQ((T2*DPOi&6_uX3mV5;^MKX;aFK3_NuvC7UNcWl#ZX$Q#qq| zgq22^DDn}*baMr>i1ypUq}-uJgg5W<7U{j00qE&X3?At{-oqe!Ro=ZDb=bB(P5D*n zS?}Vsso}m5j37ELvhZ!HBIVw*Y70>ak2Nip7xUol^vM-1EFg0MFo;s=j4Do%urKVi zfXJf3EiR|;BgP~Hz_9QJRh9FvXGa^3EnTe2(!!}QJU6E07+x@)=ef0N*uO3c3Ornt zp5DRE-kwBPPXc%FlldS&l`Hr?$$K7K5pj8P-@LfFZ{ET9u7M=(kbK_DUmD*M-<7dT zke+m>#rdpqJL}QSw(3nl4|@4a!YjEr>Mox0=e5UbBY8xxB61NGvD!+vU3n2zv0B+} z%NJ1{yMmgNu?w7Vfddy%eF@4%EGzC=5AnHtJNH|Mo3I{tl2jWugfO)G7+%GB6-3g9 zezpYGgp2HWfb2g|I0Ja(3e=NzSYC$}wX5=|UCCWn$3>?yr?;+qYaVL^zRReOfLv9jEu7HgjWo~T);!ieh1+Iu`v8Bf%VYi48Qd{YjP+cAk9{4>T*mv78N@AF*)6DKf0o=s%@YPKR=wG}7v z8b)HiH|}%2aev^A7rA?seMU**@uoS%mCM)=^%yI8Y@ETJ-9^&=Ao+QuHwn`GB}mgh z9_c8Ah&EGez#i0NFT>hO>UWWDJ6rn+?Er}!#4yz=M7G`o=o7A|)K0mCTYZ`lJiu8( zieAqkU9b@G2Ls~gX*I%iJESEk#tGhIv{^6`q4QYWLV*kwM$Y9kw$C z1oC=s2Os%HtZ>631$e!lK_XuWuU^0_MX}uV!@0_5DA8!iaP^<8@I<|(4vlqas%@sO zqW!A*F<*Yl+Bpj~x% zMc{^cE}(|@7IV3^qNr@ryes4rg$XT&=X)ir?MO)54=|wzaT^|_)^#d9$%<<*RY$1n zQDiU{a>rzl%=i1+K1^qd6kb@#BLU^}Ze<5QhvzDJL3YA9m(WIzCA96a@U)$xwkN3V zG>dJ9!JXvEVTZJR*hjuQ(Dp(~9u4q5>}Ot+sGD9DX^+pUBKx8UQf~k3l&f{C-xSmR h4A%0`y)jIO;57Mg#5p0{S4=gpXR%ZpW5PyY{ttTP4q*TQ literal 5216 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 literal 0 HcmV?d00001 diff --git a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java index dbceb22fd..3014612a6 100644 --- a/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryController.java @@ -1,63 +1,69 @@ package io.pivotal.pal.tracker; - -import org.springframework.http.HttpHeaders; +import org.springframework.boot.actuate.metrics.CounterService; +import org.springframework.boot.actuate.metrics.GaugeService; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; + @RestController +@RequestMapping("/time-entries") public class TimeEntryController { - private TimeEntryRepository timeEntryRepository; - private ResponseEntity responseEntity; - public TimeEntryController() { - } + private final CounterService counter; + private final GaugeService gauge; + private TimeEntryRepository timeEntriesRepo; - public TimeEntryController(TimeEntryRepository timeEntriesRepo) { - this.timeEntryRepository = timeEntriesRepo; + public TimeEntryController(TimeEntryRepository timeEntriesRepo, CounterService counter, GaugeService gauge) { + this.counter = counter; + this.gauge = gauge; + this.timeEntriesRepo = timeEntriesRepo; } - @PostMapping(value = "time-entries") + @PostMapping public ResponseEntity create(@RequestBody TimeEntry timeEntry) { - TimeEntry timeEntry1 = timeEntryRepository.create(timeEntry); - /*HttpHeaders responseHeaders = new HttpHeaders(); - responseHeaders.setContentType(MediaType.APPLICATION_JSON); - ResponseEntity responseEntity = new ResponseEntity<>(timeEntry1,responseHeaders, HttpStatus.CREATED);*/ + TimeEntry createdTimeEntry = timeEntriesRepo.create(timeEntry); + counter.increment("TimeEntry.created"); + gauge.submit("timeEntries.count", timeEntriesRepo.list().size()); - return new ResponseEntity<>(timeEntry1, HttpStatus.CREATED); + return new ResponseEntity<>(createdTimeEntry, HttpStatus.CREATED); } - @GetMapping(value = "/time-entries/{id}") - public ResponseEntity read(@PathVariable long id) { - if(timeEntryRepository.contains(id)){ + @GetMapping("{id}") + 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); } - TimeEntry timeEntry = timeEntryRepository.find(id); - return new ResponseEntity<>(timeEntry,HttpStatus.OK); } @GetMapping public ResponseEntity> list() { - List timeEntryList = timeEntryRepository.list(); - return new ResponseEntity>(timeEntryList, HttpStatus.OK); + counter.increment("TimeEntry.listed"); + return new ResponseEntity<>(timeEntriesRepo.list(), HttpStatus.OK); } - @PutMapping(value = "/time-entries/{id}") - public ResponseEntity update(@PathVariable long id, @RequestBody TimeEntry expected) { - TimeEntry timeEntry = timeEntryRepository.update(id,expected); - if(timeEntry == null) + @PutMapping("{id}") + 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); - //TimeEntry timeEntry = timeEntryRepository.update(id,expected); - return new ResponseEntity<>(timeEntry,HttpStatus.OK); + } } - @DeleteMapping("/time-entries/{id}") - public ResponseEntity delete(@PathVariable long id) { - timeEntryRepository.delete(id); - if(timeEntryRepository.contains(id)==false) - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - return new ResponseEntity<>(timeEntryRepository.find(id),HttpStatus.OK); + @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..49b1f175d --- /dev/null +++ b/src/main/java/io/pivotal/pal/tracker/TimeEntryHealthIndicator.java @@ -0,0 +1,29 @@ +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(); + } +} \ 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 4202b02f1..d76033f4a 100644 --- a/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java +++ b/src/test/java/test/pivotal/pal/tracker/JdbcTimeEntryRepositoryTest.java @@ -26,11 +26,11 @@ public void setUp() throws Exception { MysqlDataSource dataSource = new MysqlDataSource(); //this supose to work for the time being i use the below datasource - //dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); + dataSource.setUrl(System.getenv("SPRING_DATASOURCE_URL")); - dataSource.setUrl("jdbc:mysql://localhost:3306/tracker_test?useSSL=false"); - dataSource.setUser("root"); - dataSource.setPassword("root"); + dataSource.setUrl("jdbc:mysql://localhost:3306/tracker_dev?user=root&password=root&useSSL=false&useTimezone=true&serverTimezone=UTC&useLegacyDatetimeCode=false"); + // dataSource.setUser("root"); + // dataSource.setPassword("root"); timeEntryRepository = new JdbcTimeEntryRepository(dataSource); jdbcTemplate = new JdbcTemplate(dataSource); diff --git a/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java b/src/test/java/test/pivotal/pal/tracker/TimeEntryControllerTest.java index c6c4d2a7a..32dd1cf60 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,12 +22,15 @@ public class TimeEntryControllerTest { private TimeEntryRepository timeEntryRepository; private TimeEntryController controller; + private CounterService counterService; + private GaugeService gaugeService; - //Execute before each test method. @Before public void setUp() throws Exception { timeEntryRepository = mock(TimeEntryRepository.class); - controller = new TimeEntryController(timeEntryRepository); + counterService = mock(CounterService.class); + gaugeService = mock(GaugeService.class); + controller = new TimeEntryController(timeEntryRepository, counterService, gaugeService); } @Test @@ -33,8 +38,9 @@ public void testCreate() throws Exception { TimeEntry timeEntryToCreate = new TimeEntry(123L, 456L, LocalDate.parse("2017-01-08"), 8); TimeEntry expectedResult = new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8); doReturn(expectedResult) - .when(timeEntryRepository) - .create(any(TimeEntry.class)); + .when(timeEntryRepository) + .create(any(TimeEntry.class)); + ResponseEntity response = controller.create(timeEntryToCreate); @@ -48,8 +54,8 @@ public void testCreate() throws Exception { public void testRead() throws Exception { TimeEntry expected = new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8); doReturn(expected) - .when(timeEntryRepository) - .find(1L); + .when(timeEntryRepository) + .find(1L); ResponseEntity response = controller.read(1L); @@ -61,8 +67,8 @@ public void testRead() throws Exception { @Test public void testRead_NotFound() throws Exception { doReturn(null) - .when(timeEntryRepository) - .find(1L); + .when(timeEntryRepository) + .find(1L); ResponseEntity response = controller.read(1L); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); @@ -71,8 +77,8 @@ public void testRead_NotFound() throws Exception { @Test public void testList() throws Exception { List expected = asList( - new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8), - new TimeEntry(2L, 789L, 321L, LocalDate.parse("2017-01-07"), 4) + new TimeEntry(1L, 123L, 456L, LocalDate.parse("2017-01-08"), 8), + new TimeEntry(2L, 789L, 321L, LocalDate.parse("2017-01-07"), 4) ); doReturn(expected).when(timeEntryRepository).list(); @@ -87,8 +93,8 @@ public void testList() throws Exception { public void testUpdate() throws Exception { TimeEntry expected = new TimeEntry(1L, 987L, 654L, LocalDate.parse("2017-01-07"), 4); doReturn(expected) - .when(timeEntryRepository) - .update(eq(1L), any(TimeEntry.class)); + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); ResponseEntity response = controller.update(1L, expected); @@ -100,8 +106,8 @@ public void testUpdate() throws Exception { @Test public void testUpdate_NotFound() throws Exception { doReturn(null) - .when(timeEntryRepository) - .update(eq(1L), any(TimeEntry.class)); + .when(timeEntryRepository) + .update(eq(1L), any(TimeEntry.class)); ResponseEntity response = controller.update(1L, new TimeEntry()); assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); From d95036f48225f892c66f5173e7200f476949e96f Mon Sep 17 00:00:00 2001 From: Tyson Gern Date: Wed, 26 Jul 2017 12:50:47 -0600 Subject: [PATCH 15/16] 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 9e2965408962dd1220dfae26ac42d3e4df16a7e8 Mon Sep 17 00:00:00 2001 From: yaredDabi Date: Sat, 17 Mar 2018 17:52:43 -0700 Subject: [PATCH 16/16] spring security --- build.gradle | 1 + .../pivotal/pal/tracker/EnvController.class | Bin 1631 -> 0 bytes .../tracker/InMemoryTimeEntryRepository.class | Bin 2085 -> 0 bytes .../pal/tracker/JdbcTimeEntryRepository.class | Bin 5933 -> 0 bytes .../pal/tracker/PalTrackerApplication.class | Bin 734 -> 0 bytes .../io/pivotal/pal/tracker/TimeEntry.class | Bin 2420 -> 0 bytes .../pal/tracker/TimeEntryController.class | Bin 4301 -> 0 bytes .../tracker/TimeEntryHealthIndicator.class | Bin 1394 -> 0 bytes .../pal/tracker/TimeEntryRepository.class | Bin 541 -> 0 bytes .../pal/tracker/WelcomeController.class | Bin 790 -> 0 bytes .../pal/tracker/SecurityConfiguration.java | 31 ++++++++++++++++++ .../pal/trackerapi/WelcomeApiTest.java | 15 +++++++++ 12 files changed, 47 insertions(+) delete mode 100644 out/production/classes/io/pivotal/pal/tracker/EnvController.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/InMemoryTimeEntryRepository.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/PalTrackerApplication.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntry.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryController.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryHealthIndicator.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/TimeEntryRepository.class delete mode 100644 out/production/classes/io/pivotal/pal/tracker/WelcomeController.class create mode 100644 src/main/java/io/pivotal/pal/tracker/SecurityConfiguration.java diff --git a/build.gradle b/build.gradle index 48a0485a0..6a20b6fa4 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ dependencies { compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.1") compile("org.springframework.boot:spring-boot-starter-jdbc") compile("org.springframework.boot:spring-boot-starter-actuator") + compile("org.springframework.boot:spring-boot-starter-security") compile("mysql:mysql-connector-java:6.0.6") diff --git a/out/production/classes/io/pivotal/pal/tracker/EnvController.class b/out/production/classes/io/pivotal/pal/tracker/EnvController.class deleted file mode 100644 index 7eab781d1c865e4064fc1033768c5b213022d36a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1631 zcmbVM-A)@v6#mv;Yy%Dur-4F9OQ@583E8wI4GomU1*f%QAhIpms29=L9-K++U2Att z8lg&kj6Oyy6-8~OK0qI;>Njf>uR|_SFJ{i)oHO6~nfd46KmP`>hOfskfjgtPfKSc# znb|%!+gcKL$B@9C6uub8J*=njWeWFG*hpeCiLWf!9naSmGNl*dNH{IwHl2$0mD}8~ zFy5AJ&wpJ~ZN(##^~0hYXyMjn(QU{dEnFOE@{NWsgTO*!Q@KiSS$Mp3Hst@)EFTc& zw%3rvP*Sek>$YpsuZmiWnoP;7i`Iegm67{OTR&ICa-*a?r=yNMEm}^8OZ%e!QuR2*$H7D+rv<00}E($Sfq$C*K}FcsCA?pw&7XVU38c;)-G7qZT0 z9bYo<>%st72rAy`(zYIb!##(M$N=;;{V5Coj=l=l`1wrmZ?c&@3qdYpY7tVFBc!Yd zX$%(Yb(wUU($AzLq;t79uyQLWh~;jbAf8)2ft{PRPcZUZ=rd!^@M8mJc*lnr#Vqfe zW0kLvo2SP7i(w?nbCk?^gTx9sgC!Pn22DKV3?7^Qz^ct-^ITU0d^BJ`9O8Rq9%rA+JCIJ5hMGY4vfOg5pzYfNGe=LSqOD`OTh+SfZq@42 zt2X7X7g$cU$zR}GjUQ~kx^l=cO^b2~3_uHSaFkJD$InP(HGoUP}fvDe8uATLH0Wizfjj7i*={ zT_{hYp}>{1T*z+Pj;%t_iwi1!O>PQ^1@50om#^bAp9QCPIiR?#Q7lZh-P{aQn=3Ay ze*a7qx+7Zw>rgCp)V)p{mAv(uaq4tdRsC}f&kZc;vWQB7Cjz-;ukAPNS*eOX`i25t zSG{TC5{3o((>{%x*Ti{TFfeao0S`>nF>YcJ4-G7t_!{4sc!XsWD|l>T6>BEG#beT) z*0CmvC|NH&wlFKik`Hx$?_?snpgp2*K*qS(le&08t>>a%av)i8P@G$ z$4L}x?R87%_1K9c=W`x+8P?TfFpj;^bJ!sd>^*_=$6aIBWSAGx!|>Tl7mxJgjORGi zQ+h5_xD+FVAGMB?(kd?V^Ee2BA^xEHhtIe8rr{zCzO&W1zD>(rWI~_5@;*fQ6}0tz zWPXSKYv}zB*E~G{6FR@L1%8*`4R;E+5AR`wPqrHwBfO{d)@XabQvMD40lGL2Pk(xe ztlCyy!uSK{DhKG!V6F0kei~AaK_U*}3gcBA7b2XPgNQsCBa7hMc+56OHWgh#p^p&y z067h7Y4GaL1xz;2WK+;W0$RXTTnj-t4l(F<3_6yA_GtK-sk}%nriQ=Kt1`%RWH7=E zMydVf4zjU?Y%C@#hquH4`6~DzW>v!JD%OdSSS^*$L?l(2w*3F)x`r+e@et!ynT>`* z3@2p6F`3F<38%_Df$P*mmAw}3f&N!0tgEaJ&?m5u{*)xk3*d#c-u*Fn)JavIe~6E` i9`=!{L_U>>qWwZkRN)BiW`{2MB-DITgd3s%&Hn(#*VPLE diff --git a/out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class b/out/production/classes/io/pivotal/pal/tracker/JdbcTimeEntryRepository.class deleted file mode 100644 index 7af75955a98734d4d4a6ae4d4beb81bc26a49811..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5933 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 s)0FYU`vn+Ksg)Y<7`qmT*K1{qWB~X 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 diff --git a/out/production/classes/io/pivotal/pal/tracker/TimeEntryHealthIndicator.class b/out/production/classes/io/pivotal/pal/tracker/TimeEntryHealthIndicator.class deleted file mode 100644 index d57d41d2118e841add7e1e9049ea34dce348a5ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1394 zcmb7ETTc@~6#iyg*j|>4MNvRh5TTUH3L+pNicN`43MSALeInC#gn{kuW_K2XFa8%_ zeAPrHXyOm>HyPvEQoIlo(uX~B&e?OmbDjDA^UF5?GkB6f5~FeG7}GJHKm?;nWH6CL z7I$?_h8i;@_d+tIV_L_3hVHeM7u%)6T7EmfUfL++HyK!gp(E$mzT&njb1M)LiqJ+% zCF>$@EAMDSxDG?MC>_(42ae*F>C&e>Ufma-S^C?~m&);u78%-?q%GAlgPCr{alFhB z%{eu}&{dSSSP$wI;gxvBB2B93RJm2=o($!tGOBi^&oFgilxKolYPVq5WR+8rVUD{E z6~lCTyy$p4rtf;v-g)Kmx;S*aeY4^?%H&lQa3#!BWAhY-y!A>Tt(x$NdwRwh1=|*0 z&f>l=D5?1tQAe6Ugx;~KsJ(04ZInqvW=L#0fmao)GVI0R@3x<-YjTetaE88(zz!!^ zmcFEBt=KjVPDvUxL*~C}`AT@gQAe&Yb57lLY+(W_0I%W;b zVP3~W0}G(?%!`)kTvY~^@W{Y&NFHN=Ryi!ww79)vKCkSFs$!UHExF&Dzz|7?dpFsd zKlSmVBEp+VcA$StK2Gqw`-48T;}z4Vm(y>!}4U!$Qj|de(fx zkJg++o1v|-u^6IfBMAlr8v49L`B15L~~P0Z|9L|z^vTFiWeb`rik zCunDgy+v~3Gh$os5Ti4`MI`Z_up)#WMrb42gBa!4Ptq{O8YApK(X-9yF^u3kZc@}+ k~Vx(Fxt^4-J9&F9zq2Y^eQBsfZN%n%n&3NINZX|61<)Y5vPc}2%NQM^jWAJm(? zG2X34Svx|GrPOlm7^Tzfk>o|0lHrW1jmDd*41sRbX*<7>Wu#T@8HQ<=FT_&t#w*Qp z>WvtBGc`BDH;w{d{R77B39iYcTUg_TGWE}+pBVZx+qgnblqTQP-woc&%GSzTx4Kv- zVK{Hw`QHS?Af(sA%=z8(LKZ$|VCc|?0&&E!g$N+sMjSLjH)s$2pgY(levjx5_K7>d LfbI;3p)>pjct)A& diff --git a/out/production/classes/io/pivotal/pal/tracker/WelcomeController.class b/out/production/classes/io/pivotal/pal/tracker/WelcomeController.class deleted file mode 100644 index 145bfe0238d42311bdda05dee8554d1ecd323824..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 790 zcma)4O>Yx15Pi;PNds*I1PWY02&F};FBL*6LaL%r4jYOZlpZ)B@74`&z4pp>QdNlm z1Scd8`~ZFwV!W+rQ7&1@GoI&pGjGTK{paT|0NZ%fLKW*xJZRz}L*tb)D!gW>_BO^0 zwZ1))3@eE;^4&B~rTZXKO-LuPnb2e5RE#gdTKKFy!)Br^FVxHip?N_oIFX%7#}B2> zY%crO1ZTCDZiivXi}SZqYs>Jgmz;>1;98go9|fn(WM^X@Vrb^ldod|x%rO|&52hxl zT#l7jByQIjQWBIkq-{H~Zo+*Liyu3Y%QNdv`I$_4s>~4=|8n`B3`0>AQ3OMMCiIm0 zG0yPh|MKreEEA+#pie4=@%&h1!MgM7&Bn1j_31qQayUr(`@_Mf;b1h{-5Y#mXpQXD zW%7-RgYV7_aVw61;pM`Ab|8JYb_Z?LP-o~ZdT9)+S3}=VPh=MG7!`U&@vDGd4eDeY z6mjy5e0}o=*!S|dNw!gfO)SyAeUX40Xi>zlOh~mPzo4iRInTdEi!0`G$?RU7UNU3G oP23{R3S~#XN^9*Sm`4Y<32R3!cOvL