From c1619059b18a5ffbfd77f892847a4f45a0251804 Mon Sep 17 00:00:00 2001 From: dani Date: Wed, 6 May 2026 10:16:56 +0100 Subject: [PATCH 01/47] feat: add entities for Client, Reservation, RestaurantTable and User --- pom.xml | 4 ++ .../restoreserve/model/entity/Client.java | 33 +++++++++++ .../model/entity/Reservation.java | 53 ++++++++++++++++++ .../model/entity/RestaurantTable.java | 38 +++++++++++++ .../restoreserve/model/entity/User.java | 44 +++++++++++++++ .../model/enums/ReservationStatus.java | 7 +++ .../restoreserve/model/enums/TableStatus.java | 7 +++ .../restoreserve/model/enums/UserRole.java | 6 ++ .../restoreserve/model/entity/Client.class | Bin 0 -> 1833 bytes .../model/entity/Reservation.class | Bin 0 -> 3219 bytes .../model/entity/RestaurantTable.class | Bin 0 -> 2170 bytes .../restoreserve/model/entity/User.class | Bin 0 -> 2692 bytes .../model/enums/ReservationStatus.class | Bin 0 -> 1326 bytes .../model/enums/TableStatus.class | Bin 0 -> 1284 bytes .../restoreserve/model/enums/UserRole.class | Bin 0 -> 1210 bytes 15 files changed, 192 insertions(+) create mode 100644 src/main/java/com/pachedev/restoreserve/model/entity/Client.java create mode 100644 src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java create mode 100644 src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java create mode 100644 src/main/java/com/pachedev/restoreserve/model/entity/User.java create mode 100644 src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java create mode 100644 src/main/java/com/pachedev/restoreserve/model/enums/TableStatus.java create mode 100644 src/main/java/com/pachedev/restoreserve/model/enums/UserRole.java create mode 100644 target/classes/com/pachedev/restoreserve/model/entity/Client.class create mode 100644 target/classes/com/pachedev/restoreserve/model/entity/Reservation.class create mode 100644 target/classes/com/pachedev/restoreserve/model/entity/RestaurantTable.class create mode 100644 target/classes/com/pachedev/restoreserve/model/entity/User.class create mode 100644 target/classes/com/pachedev/restoreserve/model/enums/ReservationStatus.class create mode 100644 target/classes/com/pachedev/restoreserve/model/enums/TableStatus.class create mode 100644 target/classes/com/pachedev/restoreserve/model/enums/UserRole.class diff --git a/pom.xml b/pom.xml index a995645..14859a8 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,10 @@ spring-security-test test + + org.springframework.boot + spring-boot-starter-data-jpa + diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Client.java b/src/main/java/com/pachedev/restoreserve/model/entity/Client.java new file mode 100644 index 0000000..1790428 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Client.java @@ -0,0 +1,33 @@ +package com.pachedev.restoreserve.model.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "clients") +@Getter +@Setter +@NoArgsConstructor +public class Client { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name", nullable = false, length = 50) + private String name; + + @Column(name = "telephone", nullable = false, length = 20) + private String telephone; + + @Column(name = "email", unique = true, nullable = false, length = 50) + private String email; + +} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java new file mode 100644 index 0000000..83b1b17 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java @@ -0,0 +1,53 @@ +package com.pachedev.restoreserve.model.entity; + +import java.time.LocalDateTime; + +import org.hibernate.annotations.CreationTimestamp; + +import com.pachedev.restoreserve.model.enums.ReservationStatus; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "reservations") +@Getter +@Setter +@NoArgsConstructor +public class Reservation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private Client user; + + @ManyToOne + @JoinColumn(name = "table_id", nullable = false) + private RestaurantTable table; + + @CreationTimestamp + @Column(name = "reservation_data") + private LocalDateTime reservationDate; + + @Column(name = "number_of_guests") + private Integer numberOfGuests; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + private ReservationStatus status; + +} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java new file mode 100644 index 0000000..6eb90ef --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java @@ -0,0 +1,38 @@ +package com.pachedev.restoreserve.model.entity; + +import com.pachedev.restoreserve.model.enums.TableStatus; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "restaurant_tables") +@Getter +@Setter +@NoArgsConstructor +public class RestaurantTable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "name", length = 50) + private String name; + + @Column(name = "guests_number") + private Integer guests; + + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false) + private TableStatus status; + +} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/User.java b/src/main/java/com/pachedev/restoreserve/model/entity/User.java new file mode 100644 index 0000000..bd443e3 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/entity/User.java @@ -0,0 +1,44 @@ +package com.pachedev.restoreserve.model.entity; + +import com.pachedev.restoreserve.model.enums.UserRole; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Table(name = "users") +@Getter +@Setter +@NoArgsConstructor +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "email", nullable = false, unique = true) + private String email; + + @Column(name = "name", nullable = false, length = 50) + private String name; + + @Column(name = "password", nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + @Column(name = "role", nullable = false) + private UserRole role; + + @Column(name = "active", nullable = false) + private Boolean active = true; + +} diff --git a/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java b/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java new file mode 100644 index 0000000..4e72c89 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java @@ -0,0 +1,7 @@ +package com.pachedev.restoreserve.model.enums; + +public enum ReservationStatus { + PENDING, + CONFIRMED, + CANCELLED +} diff --git a/src/main/java/com/pachedev/restoreserve/model/enums/TableStatus.java b/src/main/java/com/pachedev/restoreserve/model/enums/TableStatus.java new file mode 100644 index 0000000..02f40a6 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/enums/TableStatus.java @@ -0,0 +1,7 @@ +package com.pachedev.restoreserve.model.enums; + +public enum TableStatus { + AVAILABLE, + RESERVED, + OCCUPIED, +} diff --git a/src/main/java/com/pachedev/restoreserve/model/enums/UserRole.java b/src/main/java/com/pachedev/restoreserve/model/enums/UserRole.java new file mode 100644 index 0000000..cb1969e --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/enums/UserRole.java @@ -0,0 +1,6 @@ +package com.pachedev.restoreserve.model.enums; + +public enum UserRole { + ROLE_USER, + ROLE_ADMIN +} \ No newline at end of file diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Client.class b/target/classes/com/pachedev/restoreserve/model/entity/Client.class new file mode 100644 index 0000000000000000000000000000000000000000..87a7a8035fff794176e2b3b8ee1a7cd85be18365 GIT binary patch literal 1833 zcmb7D*=`dt6ur*UbeYmLO+z;dWhqIwfwFg#P$?)pC5;kBhDL%A}Q6sv@0HV`?HcqrHE@ekZq4pTX^M%SN#p$U* zkLWR@(IdCzRryPyl^YA~zo8AzsJA^1f` z)4@#z8_&`l@bhRxx3z%r8a@+qhB3MFnHE1{5~CaVYY3sKCH##LKsV_YB0NjF(}!qz zFrs{4qQYQAt9^;q1|!;l9`Q>WZQ-~vEy~L37h3#;S+_{(K%`uVh(yu?hSz@&=p^h< zX()G}Hv7>p4@w`}C!&XKK>wgWePK}g(0UO)EC>3B{pr^Rr4MZz(dPnsJWy!(Z-0KJ dCl}c#d-yX(&w>lu3SQm>D7O*spnp7!-Cw5bIY0ma literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class b/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class new file mode 100644 index 0000000000000000000000000000000000000000..9c1875c2bf26f502b35a1a3a519c5dee3466f3dd GIT binary patch literal 3219 zcmb7_Z&TYw5XSe6je(e8pimN%gfwYupotoiHh)Z#riKuw22ya`CVjyi_QfcXPmFZ( z4Bx7qX{R%N(GSoM)#-C5Id)xjq`bh=>2`m2yZh|&*WZ8qNkk9nr#xjCZM593b;?^O zqAhxsCw%4NM|eG9b=|gbEFqPx&a4CTkt^GkdCD>>zU4h`Ib3$E{o}Wyr5I)GHon&f z8+BK9DvT~11YmSU)AsFSM?8_zRR-iUT7vmI?kR4a3eUHFC1gujwKm+Y$8PNiDLk%3 zyUCqEz-+=-dfhoo@W90zXQ#%awza+2s5M?O$_5CPkyTHbg{osCA2{_&?B#a3JZrf7 zQlGLCYw_H*Wz}_pu7pWe@~&W%*Oxfb$(xXY<8b6?3>6|qIi(kj?xnpSs-$?}aj6>G zQ>Vuu41FdntV#VAQijUlj;Nw8j#YPC+|i#KSi$i|-Ss-wiG3_QiLVwPl+CJo!cep+ z4(y)7emOSY_pug-4nJyh#TlKKK^OG>H#-3+5W{?U9V4omL`{VUa4|GHa^Dkq5<(71vdU!ZNA777d|xtApV9Wlsd+^4(safx#PMOhp)Y%ze{yqRWgX;T4L8Q6*Ir z@ru*TtpcskRmj8-WinqLRmo}-ir*FL#BIOe9_E9>Ttr3S5LRTP{DB40#0j`Zg{Y+V zAFYE1%>neE{4YmQ#H3&Q%))0h?Tf>K_j_F%Pr|sL?aee-jZd~ljc?l0R*x8sl~tDcHG!v=#FU_ORKN#xOtk$GRx{Wx;yLTn9L>Kb{S6%uZ}D;80y2**W?+92 zj1tyhEMo~ZN1NyHj5e-d1Coq3N?3h@_iKW#)2Fa87g!1L)7bcoKF7B*z&e&yJlF7N z;ho``Tlo6zlG2Mre9*k=oW1xfNrM)eMMg zLwb||dYlgQguYDxRnvjC=_yn=S~VSRMwe-A{Wn_w1#iP;yJ*U`7+OSS(T_blELrMeiHt1 zT71~S5&n|Fzo74v@YS^Va1KQH6@$mTgaSsDGwAovn}%I^)6=m--QA49_vRC>p00i&0pk(KDlA=$65&`n%IJRu&qC%nHT}R}%%B zSGP2pKJvg*XC>1$v3!SfTNuK$xvS9>;OB-T4E=&RuIUQK%S_K#uxzp8)B)qnF$AlW z49jDH^}9mG)ypm`uwU`=LaoT}57LKWoJ=1ajUEA{N9Q#Hs z#;c;%2R&C1RXvD=gvMK_@*L-44#30y^a=Osr_2Eyf(j4t)k1dtV~`5H_eo2Jx&Q4_tCy78m$5S?_Nr_ z)i@<-5`w$F;N$6by)z{=)>|fOcICa{AQORRL0(wk=ap<-5~L*;4FJ(hXR;O3%%%xy zX0e|Bk0c~Xblc>nc&SleI#U{?K6)L;J%f1g*!G+<+czy#<3!t4vMetPjXJklPTfB6 zFV(SzRe4}`OkQ06^>iX+-MBOlRb>^2DI`k`Q4G--qJDg(=7WreD2@TR9!!toJ0eG1 z436d%94-g5LuwYC?9v24Ff5ozA-akGDMr(DtI3e0)JK|8TivEvK<;2;d5}4L=ke(H z*YJ&GztF-b{Df!`ZwVo6dJpfo51{+>0Fg#%%5)*h_D1xO9(5sF?v3a%J?TQU(i_oJ zT7zNj8f}6+Bt^+CeWiuZ_-hquLWwl#BLb16fWG1DKu6G1ByD|08{O!$z0&&y6VRhj zK>wUJyU{QAO7B-(K%Y?bTeRJcex+A>zitEiq@qWcLc@0Luj=y~y=rGqcBsz~?I?rZ Rs9uwew2OET@}OmMzX8BOxz_*y literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/entity/User.class b/target/classes/com/pachedev/restoreserve/model/entity/User.class new file mode 100644 index 0000000000000000000000000000000000000000..cc66c183b457d1de7e165be09a4a2583f669a23d GIT binary patch literal 2692 zcmb7FYjfK~6g}(sk)(3#Je;PagaR!%E~!ES6i8ob>IQ@Bgv2#NzA5(NsFkIlhnf5< zn4ts12YvuQis7szCvIv+>IX~OyZ4^G_nvdt|NQ&c-vI98hXOJJw>wT>KQTJTvMUF= zD?Q)gL%IX0_noe^bZPshf2u#Ht$?gR>4h;cbjz@N`tH#S+3^K3W|#KH`9{OBdz%8+ z_5%j@W!v=3BTH`Cw&NSV>DZpYBHdpau5aik()CQwmv%?$^)AD1j@{akwsZ|&cH4#( zNV-jWzFPNA6C!wC>+~dys6VMSTlLnD0y)_?OiN&PB3d>$-Y<^JK4o3UV)R-gx$!~Y3;Vv$ z2%dXmY+5xt=tqsAOKzZ=Be49xu5l~!2d({jbBB2v9p4-DQU{ zY-MF+KjwES=C^JS#xOkFu$=yp^HM)!PW5h^qMBm2KGKjwUf@c@v}H5sA4#{RnyDyp zI)>FYTvM%MW7a=5shZUkT|1L2kDFxrwTS8Y*D8p)R+-$JHnK-0ZdBg3LWMg;v|(in zqh}8bQ%?DNrfvEU1STpg?P*NlI}P8UK8sQlJMyWic$!id*d2u?Fn+#k;YCN$ER7wWP>5pR}mq&Lg9Q%vFJ_ z8LlSzR-{c!T!Kae)fI1-`M$!il1JmrA!admhTLo)43z~&Q*WM ztzY?(!N+{6vVl2N_|zf?tf0yj|5)%z0?=wYAUzRiEgjH$BG5)UpwIC|LTPu?0eyw9 z6M#0;0o}&~%IYF(3Ou7|s@{HuTfgyZl(zYhHYF`ZWJH_#iq!m&)*RJNX$m~TRucSb zTKH%hV)$YR|2P?bEiHUBNilpWgx^kv-$)A|O<@dwHH3eHT2gcFriG8jIEG&g;h$nB z34Svzd^G$q{Bj7-+m;HrsGNtisnIT0|G>A^?4M}7!pJF?nGsM%mb0$$eLnxiSV$S{ V(<6g}@LE)tP>bv51m&&O{trY;;3@zB literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/enums/ReservationStatus.class b/target/classes/com/pachedev/restoreserve/model/enums/ReservationStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..059cbfbb93132198d682d149b65619bc253236a8 GIT binary patch literal 1326 zcmbtTT~8B16g|UkKUts_t@7b_m9`)&Dj01gkWx%TTL>#n8lQ%CgeBWuvfVb}&7Y*@ z0iu!k?2j_u*;Z&me5sr4&YYPu=bn4#?yuk9E&#m4N(dtiuj@`zKj!sA(GVTo6)n%9 zk8nFeZ#oTO>B4R|Tl%iQ#y!)q4Uc>6RtPG?_z~}L-QxCvULYDnXs1xwC|2GxyeSPS zoMnjQw=0{)-Ev_=&etpXLa8L@qlL;|IaysV?G+4$w;%r_L^xNsOxyHU8I)AI%An>c z+b{xAFvK+6K%60%>r)b`(p9F0=bH9GwwSI)hw1zN(ayF|W zf+)j8$+ShK-P{-M2fl9!hIq-TbF0c-Q(}KH3I#Fp;~knUhGzWUNiLNhzCKaRAss@BLJZ`6((*)8Lk9D7E!=haN!>X< zVaNUBd#NGb|4(=Pll9We4nB zhPp~kHZS#^+ADL^aN2HNY?|_h64yF^QJ$j;80LXZe}YS-tpRzdl^Z&Z)$H05b&NuP zsJb>KTMM9(y2>A^p@7)5Y$wT}1{;M&{4qjJ!tz=F4pxhgD`%KE#iS2zsXj>f;7-5? zQ^6m^Q@WTmYHAk=qZa65$~eW$R}vY)UHZm-NtNUSn5NKkWbB^5D;e~U&{u|DqIGFG zl$oDB!~H9PAbCUokn~9uJdioyF};xyJd6R4=#3zWC;dcLD4JADbTL!gQZg5{oU~e2 zX3p_+;S25b-1WrSrK1Sl{TSo{h0?{m^hfMMyg6n^2$zr-tlT>b-o Cgg+ht literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/enums/TableStatus.class b/target/classes/com/pachedev/restoreserve/model/enums/TableStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..9689e08449201edd9e291e8901bcb13f30488abc GIT binary patch literal 1284 zcmbVMTTc@~7(GMpWszF6%Efz?wje8Vd1)oox|+sv2`fz+pN8!SOZGyx+a|pEle9cQ zG!mcvQO0k!752pkyUEVXH}lPz^PM@nzkYwa1n>&$35+o;8?LRMaN|g{MNjoa$9Jg_ zUQejD+ZL88oUYwbKkx%f=sx$modlu`H;#Fas}^?-RgDrDQq^X)-l)EAXbdZj{{}5H zB=FYaa+NP>&(oa1CxXxo1i>gUK`^$Pgd@$m}qr^HS}+!)?JZF$i3*Z93c{o?F5{ za@)Jyqh()slppVLt1D=*UMP;DOd9hjCQu;oP{pSmU)TytSfFd=p2trO_vDnJ6xL%5N}P3bk2mly(D)~)w+4;kk>t0)I)IU_>fU37C=S_q(? z+!OR<93V3zZ6hLV)<0RgSE0ZBIlSq!&DI5js`(42=2y$U^@PT@*;gq z>8)rVIlUF@V_HAM>{kLA!#!%V0c4cmW0;|%=ZV<;U{z+&w?M5tdYR@W%U z0pi3B!%dPWrQo5g0Z-^njp0!Scua2!c|08!vQDQ-v|Jyvt@n}AWve2oZbfD<@NDr5 wtqj-=z}Xd}6y5mqA)^xY^0;)Cl`gRK6YSsnB$XKb-#$y=mjV0=R)c=+4|bL;WB>pF literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/enums/UserRole.class b/target/classes/com/pachedev/restoreserve/model/enums/UserRole.class new file mode 100644 index 0000000000000000000000000000000000000000..843cd7554767544d7663c3f9824838dcbe982140 GIT binary patch literal 1210 zcmb7D-A~g{6#reity}3}P67Gwd+IiDWg30iBp}X&xPhT=$>M{#bXO?pTGDk(c=JzE zcz|dmKKn-*&+P{E#fLSyALpKPe&=^S`seSD3jnY2T*U-K$@03!5w{LRM+^*K^a77I z;SYq-^*X{agxl}-j3zzpdX7*LW0*SR18z9n-8X8aWk~Jr)N7xcW^I>YwLTtVg+UA5 zH{Le3wizaC+s#IlQKynu7P1%M+P?-64jB( zY`%V-jv4s2yIQIr8x7#|BND)-scExtTyC?jQe9sXK*}7+O zr^S6+%Hg911=B)cWjqC44H-q4!q6>?vpwYKG`P*-02It zyvP^Eu7rk1c&s8v$dL?>dx7Zcc!H^FI=t7bL|9x!xXwt9APrG$SX9mQ`o1OJ*|Hz$ z%dsst&h-g?=VweO^uqKqn0ekXeu;LYf$z8Yan3 zC#glU{7OH;+S#;nhN)9zL*Z5|6lUVTNTr-0Yqn!2m@!W=_nkZ@Fi-nb7%hf0MO&cz zLa32inrYf)j557TPHJ&^=?sh4&IuwxR6{2{ysKD}WO0vHY65pMz+GA?+{c3v<5v_( zqRpOQuKivqUbL%{U_)6v$HSFxq!}_Bg0m||DQae6#HdK846S8Z@f^k9VE^}%RN@TJ PzDnRy2yb9DJj?$8o!=Za literal 0 HcmV?d00001 From de0f5925d27dde364b93620bae96ad9a1e0f95fc Mon Sep 17 00:00:00 2001 From: dani Date: Wed, 6 May 2026 10:46:35 +0100 Subject: [PATCH 02/47] update repository --- .../repository/RestaurantTableRepository.java | 15 +++++++++++++++ .../service/RestaurantTableService.java | 7 +++++++ .../repository/RestaurantTableRepository.class | Bin 0 -> 607 bytes .../service/RestaurantTableService.class | Bin 0 -> 359 bytes 4 files changed, 22 insertions(+) create mode 100644 src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java create mode 100644 src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java create mode 100644 target/classes/com/pachedev/restoreserve/repository/RestaurantTableRepository.class create mode 100644 target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class diff --git a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java new file mode 100644 index 0000000..5c91073 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java @@ -0,0 +1,15 @@ +package com.pachedev.restoreserve.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.pachedev.restoreserve.model.entity.RestaurantTable; + +public interface RestaurantTableRepository extends JpaRepository { + + List findByGuests(Integer guests); + + // TODO list by Guests, zone, date... + +} diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java new file mode 100644 index 0000000..9f3879c --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -0,0 +1,7 @@ +package com.pachedev.restoreserve.service; + +public class RestaurantTableService { + + + +} diff --git a/target/classes/com/pachedev/restoreserve/repository/RestaurantTableRepository.class b/target/classes/com/pachedev/restoreserve/repository/RestaurantTableRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..eb2246707ede7320b93369744f54ff3e92decfda GIT binary patch literal 607 zcmbVKOHRWu5FHoV&{7~LV21=7ykZsTf&~Z^1Zvr%X)<*kQaf^-qMn1>u;2h33NihN zMnaWP7ar@)$Zy`8@z?jqCjeYQ7(hcnFR>F@U^1qZrqWUAEeCQ_Vo=ypGoHyl=dp5V z{4)-7>i<>(*cA|FI7OMGQSxz^QQ`w=3AnIM$+B?TsFA}7y;=7vQ}iga0_)`W1+EDN zbVu5x*R$J-dzJ!DPUAKGo$;i|MQ6)g<#jG&UHV8sd!Us;UpW%+@}D=C@n-82n^G>x zcQ-D}fc+6N3<=}XHau&$75jc1;ka~0 z*Guhf9RF*kVQqJW>+-+(ZV1J~wchZ9t(kE0_ZpXR1Drzg%2gnhVoqT$O7$AZF5a;B aDn^1r7~7B!2f|VR&ZHiSA{+@5jK2XDO Date: Wed, 6 May 2026 12:28:59 +0100 Subject: [PATCH 03/47] fix: improve fields in Reservation and RestaurantTable --- .../model/entity/Reservation.java | 7 +++++-- .../model/entity/RestaurantTable.java | 5 +++++ .../model/enums/TableLocation.java | 6 ++++++ .../model/entity/Reservation.class | Bin 3219 -> 3483 bytes .../model/entity/RestaurantTable.class | Bin 2170 -> 2601 bytes .../model/enums/TableLocation.class | Bin 0 -> 1238 bytes 6 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/model/enums/TableLocation.java create mode 100644 target/classes/com/pachedev/restoreserve/model/enums/TableLocation.class diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java index 83b1b17..a16e198 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java @@ -33,16 +33,19 @@ public class Reservation { @ManyToOne @JoinColumn(name = "user_id", nullable = false) - private Client user; + private Client client; @ManyToOne @JoinColumn(name = "table_id", nullable = false) private RestaurantTable table; - @CreationTimestamp @Column(name = "reservation_data") private LocalDateTime reservationDate; + @CreationTimestamp + @Column(name = "created_At") + private LocalDateTime createdAt; + @Column(name = "number_of_guests") private Integer numberOfGuests; diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java index 6eb90ef..c2661f9 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java @@ -1,5 +1,6 @@ package com.pachedev.restoreserve.model.entity; +import com.pachedev.restoreserve.model.enums.TableLocation; import com.pachedev.restoreserve.model.enums.TableStatus; import jakarta.persistence.Column; @@ -35,4 +36,8 @@ public class RestaurantTable { @Column(name = "status", nullable = false) private TableStatus status; + @Enumerated(EnumType.STRING) + @Column(name = "location", nullable = false) + private TableLocation location; + } diff --git a/src/main/java/com/pachedev/restoreserve/model/enums/TableLocation.java b/src/main/java/com/pachedev/restoreserve/model/enums/TableLocation.java new file mode 100644 index 0000000..377be2f --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/enums/TableLocation.java @@ -0,0 +1,6 @@ +package com.pachedev.restoreserve.model.enums; + +public enum TableLocation { + SALON, + TERRACE +} \ No newline at end of file diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class b/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class index 9c1875c2bf26f502b35a1a3a519c5dee3466f3dd..7174af71ba15e6cabe9e98623cf8d8545a151710 100644 GIT binary patch literal 3483 zcmb7FZByGu5MG&gqQnLYC4q*7CaobQYG~4iw=~oc;>17-uG^+B?qOey68pr+5;Od( zcBY-q^h19@e^jQ=p3cE_)RFSRmhN`BmrX~izzxov1HCB%*Hc5(Z&B{0I2BG)sD`tD^itLWi!yE<`=xV3 z$y1Fri!L98z-Y;e+jftda#JZU&_HdArs4id_n@GCfYotBEFWm$5?NY;68-a?f>vRqG-U7LC+=$=qzB zqDv*uZ#c*9k@OYz9C1#&Q(!kH1P3AA)(K7Q~a8>dCugUi4v>(8RAu|;B6qqf1r z@j}+BuOqYz8nqKS3d*!@QKQibqpg%{Tk=r&6(4a-x zlB?vNE}Z5bM`=nc!gpBs61j@q_qj;=*@iu7dBTB_^Ci;*#lHqFzAP*6Kk&_uphKnoQ#!>$aDz4|lZ zW2OX{rbq$-9kQ15@5mKn4g=3d8Y2&*xyhVf*Lj7qvdHa5^(#{3>u zS(tNpj z2FAF7_nVSFqK^@*GnHCj&q+DHfVWg^hSbUv=6mthAGGuv-n#j}qWPbSJPiM=ji^sb@E*YEL>E}JOM6N1_tL^g zeP-YHviQHLA&X$^mv4F51Kd^9Hv{H%sA(>F=k zZ>5EgCXIog)9@AgHVOW5TKH&Q8Tfe(kAV+?^$PQRF8xW*d(mfN=2C`!)aw07&u^Zg NKVw~mG%;X*`4_7j*xdjC literal 3219 zcmb7_Z&TYw5XSe6je(e8pimN%gfwYupotoiHh)Z#riKuw22ya`CVjyi_QfcXPmFZ( z4Bx7qX{R%N(GSoM)#-C5Id)xjq`bh=>2`m2yZh|&*WZ8qNkk9nr#xjCZM593b;?^O zqAhxsCw%4NM|eG9b=|gbEFqPx&a4CTkt^GkdCD>>zU4h`Ib3$E{o}Wyr5I)GHon&f z8+BK9DvT~11YmSU)AsFSM?8_zRR-iUT7vmI?kR4a3eUHFC1gujwKm+Y$8PNiDLk%3 zyUCqEz-+=-dfhoo@W90zXQ#%awza+2s5M?O$_5CPkyTHbg{osCA2{_&?B#a3JZrf7 zQlGLCYw_H*Wz}_pu7pWe@~&W%*Oxfb$(xXY<8b6?3>6|qIi(kj?xnpSs-$?}aj6>G zQ>Vuu41FdntV#VAQijUlj;Nw8j#YPC+|i#KSi$i|-Ss-wiG3_QiLVwPl+CJo!cep+ z4(y)7emOSY_pug-4nJyh#TlKKK^OG>H#-3+5W{?U9V4omL`{VUa4|GHa^Dkq5<(71vdU!ZNA777d|xtApV9Wlsd+^4(safx#PMOhp)Y%ze{yqRWgX;T4L8Q6*Ir z@ru*TtpcskRmj8-WinqLRmo}-ir*FL#BIOe9_E9>Ttr3S5LRTP{DB40#0j`Zg{Y+V zAFYE1%>neE{4YmQ#H3&Q%))0h?Tf>K_j_F%Pr|sL?aee-jZd~ljc?l0R*x8sl~tDcHG!v=#FU_ORKN#xOtk$GRx{Wx;yLTn9L>Kb{S6%uZ}D;80y2**W?+92 zj1tyhEMo~ZN1NyHj5e-d1Coq3N?3h@_iKW#)2Fa87g!1L)7bcoKF7B*z&e&yJlF7N z;ho``Tlo6zlG2Mre9*k=oW1xfNrM)eMMg zLwb||dYlgQguYDxRnvjC=_yn=S~VSRMwe-A{Wn_w1#iP;yJ*U`7+OSS(T_blELrMeiHt1 zT71~S5&n|Fzo74v@YS^Va1KQH6@$mTgaSsDGwAo5Psu ze((qQqa4p}60MzTf*;ysv*(_@d+xdS`;V{Rh-ih%{gh#}P;;BcfmPe%b>1>O?n@Vc z+-q^8>DIYpa3O7ZXl!H03Oq~5inZ(Te(Gg3{N8F=hGU6_@oM)yuSrH3yN>Ur-y0=Y zG?p1nYzN?J@~Z9I2wxMzm6o(!;WIi7_`c;y%Q)bkZ~Kyq8aK>3!lt`!ZE?XpOY(Zv zasm$6F<+{6<1irt=T#05bcFeIqg*j7Zy5CotH~MV+mhaqo~_e9+x7f)*9n>e=k_^V zG~`|n^t^*rBY;S_&}bW#DUcWU06rdWzY{@omwSLiP#FMzx0GCipy?Z0_#GVtx(i)X z+W>p04ItT;(udKxzd|E5`gSVYW_b%jiokJHebto+YsanWGBKJzN|vQ4OeN9a(u67U z#hEr)B3n#GwlGDji!t&g$8GMq`^K+=gRgfTwj4liS8|l4K1L@?w%}zgs#Zvq6307L z%d^#0qL00D&xY7bDX!7Y@Hn|2Hz{kcjQw$!$So?Tn>jj8Cm0O?x)r&h@CP{dgF@fV zQJzLabXZr&4D`|MpxEYfRG@K2LpVFupV8vc_~O82#dtqQQ*;g`^urR47CS3AQ$@GG z1mSL@dyFJ0{jA2F1E86#qjOMKXkw;#;BoN1}=2 zmK9OoLtEH#l~GS|raDMH^ejgkw8?02#|^w1-?Sa{+E~Y+yro7Vqwa%bL(f)NYqD#H zYUg5iM2+Qe_`Z|M(mq{8c~p;PDWkdotA4CTusVjPD&8QcAj??<9Skj@$qrk z)NmLu+8xrnm{UHD0R%5a8ds#OOw%9n)~BIht=%+M2#turk`z*40!ny<)RV> literal 2170 zcmbVMYflqF6un#e;86q$MX-o~sBKYJK@b5S2!%AMXrO7LU&zu<+J)^b*_~~|ztTiw z;s<|#KgxJ!b}OZ+Y4yWC?#!7pbI-YVe*XIYgNRvn}%I^)6=m--QA49_vRC>p00i&0pk(KDlA=$65&`n%IJRu&qC%nHT}R}%%B zSGP2pKJvg*XC>1$v3!SfTNuK$xvS9>;OB-T4E=&RuIUQK%S_K#uxzp8)B)qnF$AlW z49jDH^}9mG)ypm`uwU`=LaoT}57LKWoJ=1ajUEA{N9Q#Hs z#;c;%2R&C1RXvD=gvMK_@*L-44#30y^a=Osr_2Eyf(j4t)k1dtV~`5H_eo2Jx&Q4_tCy78m$5S?_Nr_ z)i@<-5`w$F;N$6by)z{=)>|fOcICa{AQORRL0(wk=ap<-5~L*;4FJ(hXR;O3%%%xy zX0e|Bk0c~Xblc>nc&SleI#U{?K6)L;J%f1g*!G+<+czy#<3!t4vMetPjXJklPTfB6 zFV(SzRe4}`OkQ06^>iX+-MBOlRb>^2DI`k`Q4G--qJDg(=7WreD2@TR9!!toJ0eG1 z436d%94-g5LuwYC?9v24Ff5ozA-akGDMr(DtI3e0)JK|8TivEvK<;2;d5}4L=ke(H z*YJ&GztF-b{Df!`ZwVo6dJpfo51{+>0Fg#%%5)*h_D1xO9(5sF?v3a%J?TQU(i_oJ zT7zNj8f}6+Bt^+CeWiuZ_-hquLWwl#BLb16fWG1DKu6G1ByD|08{O!$z0&&y6VRhj zK>wUJyU{QAO7B-(K%Y?bTeRJcex+A>zitEiq@qWcLc@0Luj=y~y=rGqcBsz~?I?rZ Rs9uwew2OET@}OmMzX8BOxz_*y diff --git a/target/classes/com/pachedev/restoreserve/model/enums/TableLocation.class b/target/classes/com/pachedev/restoreserve/model/enums/TableLocation.class new file mode 100644 index 0000000000000000000000000000000000000000..4a1f31b4772f651b82b071f50fe0a97e0499a053 GIT binary patch literal 1238 zcmbVL-%k@k5dN0C_S)lsS_P!w@2YJE~#@TCy^TC%kx!s+enQy-NcKYl0w@UzT@Iu2F!%N%mn8(~c6m8Kr1JMnA zT13znX2)*}*A!l_(=~VbzAI|J%|plcG^h-dN4(EXmwN|hl|mR2)<&&SXLwZ`0kX`X z?N)boHa4pa@#Eyj5(bDfXs;1d=Ie(wH%zAt_I{4Wy7JsG8%6datuDf=DKYtW?Tv z^1zX@MVC4hOc90f(CIShc`0k&;~l{;F);j~?ReZ}$ZU)7&~Ja>0l7mFP<*n_-JYPs z>q2ofIdnY2V-0zNkF|Ny4MoSm6Fg-|^B~|SwtsxWP>PGu*gq0>Sl){1^Y@AbqKAvf zH9j$H#nXlw7DswLDc7Qb0+wi?;;5T0?{>>EDpzH$+miE01Emh+)h)jl*y63@lGfRO z26u}%pOe!A&`yH`WO8zB$fMG(W1Qj) zvYKSe-|}~`R(3);$K)BZk#S3njOoM=aw(_CS}pYy)7BYgzf#5+=4hRas;QWuYB@UR zB9HXaoYN|6tkAvWq?HyI&M|+ToFo!NHA*s~vxWsp7We2&kKs-RxJzFe_wiuJc$F$i zw3$=Pw%#kH%T`4a+*al<@Nnr1c?Qe|;OvS~nx3^dWK^PEme#YZbb-=Ou>a>tDhY;X PpC#~e1aIPbw6FXDs$VCX literal 0 HcmV?d00001 From 0d4abc337b7664ee985055cd5b33cd7be91c41f9 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Wed, 6 May 2026 21:30:26 +0100 Subject: [PATCH 04/47] feat: implement basic restaurant table CRUD --- .vscode/settings.json | 3 + pom.xml | 2 + .../controller/RestaurantTableController.java | 54 ++++++++++++++ .../dto/RestaurantTableRequestDTO.java | 14 ++++ .../dto/RestaurantTableResponseDTO.java | 13 ++++ .../restoreserve/exception/ErrorResponse.java | 9 +++ .../exception/GlobalExceptionHandler.java | 36 ++++++++++ .../exception/ResourceNotFoundException.java | 8 +++ .../model/entity/RestaurantTable.java | 6 +- .../repository/RestaurantTableRepository.java | 12 +++- .../service/RestaurantTableService.java | 68 +++++++++++++++++- src/main/resources/data.sql | 11 +++ .../RestaurantTableController.class | Bin 0 -> 3156 bytes .../dto/RestaurantTableRequestDTO.class | Bin 0 -> 2704 bytes .../dto/RestaurantTableResponseDTO.class | Bin 0 -> 2249 bytes .../exception/ErrorResponse.class | Bin 0 -> 1668 bytes .../exception/GlobalExceptionHandler.class | Bin 0 -> 3893 bytes .../exception/ResourceNotFoundException.class | Bin 0 -> 476 bytes .../model/entity/RestaurantTable.class | Bin 2601 -> 2605 bytes .../RestaurantTableRepository.class | Bin 607 -> 1483 bytes .../service/RestaurantTableService.class | Bin 359 -> 5122 bytes target/classes/data.sql | 11 +++ 22 files changed, 241 insertions(+), 6 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java create mode 100644 src/main/java/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.java create mode 100644 src/main/java/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.java create mode 100644 src/main/java/com/pachedev/restoreserve/exception/ErrorResponse.java create mode 100644 src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java create mode 100644 src/main/java/com/pachedev/restoreserve/exception/ResourceNotFoundException.java create mode 100644 src/main/resources/data.sql create mode 100644 target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class create mode 100644 target/classes/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.class create mode 100644 target/classes/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.class create mode 100644 target/classes/com/pachedev/restoreserve/exception/ErrorResponse.class create mode 100644 target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class create mode 100644 target/classes/com/pachedev/restoreserve/exception/ResourceNotFoundException.class create mode 100644 target/classes/data.sql diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 14859a8..902314d 100644 --- a/pom.xml +++ b/pom.xml @@ -30,10 +30,12 @@ 17 + org.springframework.boot spring-boot-starter-validation diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java new file mode 100644 index 0000000..28bc52b --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -0,0 +1,54 @@ +package com.pachedev.restoreserve.controller; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.pachedev.restoreserve.dto.RestaurantTableRequestDTO; +import com.pachedev.restoreserve.dto.RestaurantTableResponseDTO; +import com.pachedev.restoreserve.service.RestaurantTableService; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("api/v1/tables") +public class RestaurantTableController { + + private final RestaurantTableService restaurantTableService; + + @PostMapping + public ResponseEntity create(@Valid @RequestHeader RestaurantTableRequestDTO dto) { + RestaurantTableResponseDTO response = restaurantTableService.create(dto); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @GetMapping("/{id}") + public ResponseEntity findById(@PathVariable Long id) { + return ResponseEntity.ok(restaurantTableService.findById(id)); + } + + @GetMapping + public ResponseEntity> findAll() { + return ResponseEntity.ok(restaurantTableService.findAll()); + } + + /* + * TODO + * · update + * · delete + * · findByMaxPax + * · findByStatus + * · findByLocation + * · findByLocationAndStatus + */ +} diff --git a/src/main/java/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.java new file mode 100644 index 0000000..dc86759 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.java @@ -0,0 +1,14 @@ +package com.pachedev.restoreserve.dto; + +import com.pachedev.restoreserve.model.enums.TableLocation; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record RestaurantTableRequestDTO( + + @NotBlank(message = "la numeración de la mesa es obligatoria") String name, + @NotNull(message = "la cantidad máxima de comensales es obligatoria") @Min(value = 1, message = "Mínimo 1 comensal") Integer maxPax, + @NotNull(message = "Es obligatorio introducir la zona (SALON o TERRACE)") TableLocation location) { +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.java b/src/main/java/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.java new file mode 100644 index 0000000..6649a58 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.java @@ -0,0 +1,13 @@ +package com.pachedev.restoreserve.dto; + +import com.pachedev.restoreserve.model.enums.TableLocation; +import com.pachedev.restoreserve.model.enums.TableStatus; + +public record RestaurantTableResponseDTO( + Long id, + String name, + Integer maxPax, + TableStatus status, + TableLocation location) { + +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/exception/ErrorResponse.java b/src/main/java/com/pachedev/restoreserve/exception/ErrorResponse.java new file mode 100644 index 0000000..2de6655 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/exception/ErrorResponse.java @@ -0,0 +1,9 @@ +package com.pachedev.restoreserve.exception; + +import java.time.LocalDateTime; + +public record ErrorResponse( + String errorCode, + String message, + LocalDateTime timestamp) { +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..d458119 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package com.pachedev.restoreserve.exception; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + /** + * Maneja el error cuando no se encuentra un recurso. + */ + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity handleNotFound(ResourceNotFoundException ex) { + ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage(), LocalDateTime.now()); + + return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); + } + + /** + * Maneja errores de validación de los DTOs (@Valid). + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidation(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getFieldErrors().forEach(f -> errors.put(f.getField(), f.getDefaultMessage())); + + return ResponseEntity.badRequest().body(errors); + } +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/exception/ResourceNotFoundException.java b/src/main/java/com/pachedev/restoreserve/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..9bbd774 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/exception/ResourceNotFoundException.java @@ -0,0 +1,8 @@ +package com.pachedev.restoreserve.exception; + +public class ResourceNotFoundException extends RuntimeException { + + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java index c2661f9..26878f6 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java @@ -26,11 +26,11 @@ public class RestaurantTable { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "name", length = 50) + @Column(name = "name", length = 50, nullable = false) private String name; - @Column(name = "guests_number") - private Integer guests; + @Column(name = "max_pax", nullable = false) + private Integer maxPax; @Enumerated(EnumType.STRING) @Column(name = "status", nullable = false) diff --git a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java index 5c91073..ed29c2c 100644 --- a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java +++ b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java @@ -5,11 +5,19 @@ import org.springframework.data.jpa.repository.JpaRepository; import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.TableLocation; +import com.pachedev.restoreserve.model.enums.TableStatus; public interface RestaurantTableRepository extends JpaRepository { - List findByGuests(Integer guests); + List findByMaxPax(Integer pax); - // TODO list by Guests, zone, date... + List findByStatus(TableStatus status); + + List findByLocation(TableLocation location); + + List findByLocationAndStatus(TableLocation location, TableStatus status); + + } diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java index 9f3879c..a40c71b 100644 --- a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -1,7 +1,73 @@ package com.pachedev.restoreserve.service; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.pachedev.restoreserve.dto.RestaurantTableRequestDTO; +import com.pachedev.restoreserve.dto.RestaurantTableResponseDTO; +import com.pachedev.restoreserve.exception.ResourceNotFoundException; +import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.TableStatus; +import com.pachedev.restoreserve.repository.RestaurantTableRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor public class RestaurantTableService { - + private final RestaurantTableRepository restaurantTableRepository; + + public RestaurantTableResponseDTO create(RestaurantTableRequestDTO dto) { + + RestaurantTable table = new RestaurantTable(); + + table.setName(dto.name()); + table.setMaxPax(dto.maxPax()); + table.setStatus(TableStatus.AVAILABLE); + table.setLocation(dto.location()); + + RestaurantTable savedTable = restaurantTableRepository.save(table); + + return toResponseDTO(savedTable); + } + + public RestaurantTableResponseDTO findById(Long id) { + RestaurantTable table = restaurantTableRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + id + " no encontrada")); + + return toResponseDTO(table); + } + + public List findAll() { + List tables = restaurantTableRepository.findAll(); + List response = new ArrayList<>(); + + for (RestaurantTable r : tables) { + response.add(toResponseDTO(r)); + } + + return response; + } + + /** + * Convierte una entidad a un DTO de respuesta. + */ + public RestaurantTableResponseDTO toResponseDTO(RestaurantTable table) { + return new RestaurantTableResponseDTO(table.getId(), table.getName(), table.getMaxPax(), table.getStatus(), + table.getLocation()); + } + + /* + * TODO + * · update + * · delete + * · findByMaxPax + * · findByStatus + * · findByLocation + * · findByLocationAndStatus + */ } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..18ec77e --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,11 @@ +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 1', 4, 'AVAILABLE', 'SALON'); + +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 2', 2, 'RESERVED', 'TERRACE'); + +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); + +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); \ No newline at end of file diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class new file mode 100644 index 0000000000000000000000000000000000000000..2593801138e8e4abde4148d28f82f74a0c99c951 GIT binary patch literal 3156 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 literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.class new file mode 100644 index 0000000000000000000000000000000000000000..ce2377da9a61da3e77dab6e3e825299357009e1f GIT binary patch literal 2249 zcmbtUT~pge6g_LOWgAhz0a~{-&@?o*NTQ@^nzRb}f+lh6giK+0Xr6?<14?Acm1HLU zH~j&9XlCl^3=jPQ{ZXABeHa$Uc!tTt-7D?hb3e}Bz5o7q^bde_JlBvDxZCnOX4h^V z$hJH*0~tm>7a1H%vmN>7&)l$kf$c>*_MR*G+x5LrZtiSpm=u_OZ6DgEYkT{&Z23W3 zAm?zIZJr!7eQ)0qnDp$96sRaC+fm>oXN8Xa_NDz+;DU16@FKY{140YTA?k$!x0+{X z+wt4dHKo_2==v=?a(qwV_IVsMGZ31!wni}Ohk@$?TOSZ=ly3&V=Xg$Z zU*LB&c+y)6AfrLq8T7*#n$LO*=YbsGczw5w34E=?#I58CcKN=a%jm#wzqA8B>PQBp zu)OW}f|h*ZxV)(4v**V3crwt#W8aU$D6qS!tFVmkU}^ZC;FH-A^t{OFNHe{X%u>fa ztP7O(1|9_7sT4Ziq5npjsr574Yr8VM-1Pl7y{@J3_&&24aObzK+>~J}aJn3ZDrCd8 z!%*GGe7sWJj);D$ue0u&Dd27q4+SndZ7Uv)RyqT%bk9!$kZ`pj?-{Z%N5oem#-N|xC9J?gaTT{S6LhtbOrch zh&2^tU@ZgH7%4}~qOo>_tK(3D&-n&zXKDSAI=<8u<8RzF*7}$;uJ*BLT!R%%mZ`n8ZOh&iPTMjap&-GK+v8oXHMzN~Z3^A8>kMfjwN|aVV8~bd=Uc9A zn7b7Qr6nAP@6vJFHE8YfR-0keSCta2YU#YW#$B;ZJ3hPw+Fj5S~?p*wK!x|CR#4byN}7@mh&gVcv0CnOQUFYROy#~l^7G0i|h*`Zo{Bix2nf6r~+ z60Wexx6GE+v32pv@N#{kJm+QQ5$J_wxsGe|wvTo)n1iNZo?)V2n|8CM!9G%KVrfDDZnXg5VO zqj{kG9qKn)#Bhq$U6B@&W54N*dn6mkE}GJG16I))6I%8I;GU(Enu=E3_|Ae*NtFx%D#p2ix9*XaE2J literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class b/target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..5219c4ac74f73170b642974a7e71d2887a76e37f GIT binary patch literal 3893 zcmcgv*;W)+6y2AppeX@~LyQwqL|TcZF(yhQ$|xoc7{E@5NlbP3r70+?+8RJIn0ZdV zA^C(P4?%RTd_Y#dDJ#3GM?#@(vX&2B!>xOUd(J-loc{Cg2Y&##j2{)$3G`>|6)mr4 z7G+khYL0Y0n-}SFRmUhjw)4DlUdY{m(tSKD@4FVlY`l_y(x|P$W(@Qer z2{a8Dmf;Nw)c3|y0u3W}R>n||78Q+X5@=5vmYnie($bmJ)20;YOxhXUOzDmhycZW6 zyhXzm7)bti4-*1yi=neqwl{A3R#srCw7MWKkUu>&!?lHM%yDd|G+CF z8Lq)R3|W@#=>Y(khW^M?ugSEQHmt0sm-lMrNEKm1#f_`F=`#dZ{%7bF>=!s!3m&!{ zt{DfIimB`@?TRjI|K*qq-;O_@<&8Cs;6+y(Vz5q+|O&s@jK>K^^r9TR9QvxSzL~{%Ja7Muk0>^ihm|zji5;DH4;s7*(6OpJ1Yo)T6hl=A|aHtmr2CENM zV3b5HN&=Ee1awm;?-Fn_0Jp&TngF*2EQD*vr1Tc;>(hB3lavR;SDS*%Q!wr{dyon^&E zX(nwI)2(fhNVH-M*A$Ek^gW53iV0k2Pof9ohBUJw@QAL?QpJZyPa3Y5=wy$6O+^xu zY^ZuB=)?kNip5b%4>44U*n)3m!fri6!Y1Y%dO3~P72Kf3%Ca5D zXZ7y06}QXTd>jTVm?6`}AdyqBAP|oVU=?&LVk#Dcj@4YkU!Y@2#d5H?n~S4zL1#e} zyICt%U@Nc$c2(fRu&Bu64x3fpXZ|ln#J-9miVR-e6pKp&g1(=Af-@6&v*AoOZUjT) z!RV>`;{YGh>41cq(fb3I4T7l_=AVI3bRcwgXH z7>4Qth2W-||y%8mMhp5X+&uHr*{B+$zKQCbrCt2$Oi$%692sN{AhZF||zC#nLt zoprYe&JmH-+iU+=F!^ZN>a-O#kLad3Yj`rz-UK$vGms=;}FWjS-@C0yX{m$|=@G|L<58W+nxU5fCjKlJGy#R8vt9wITnfy%}2>L{Agg+n-2_FwDcb3 zgV+^T^erxT!~zKMV~~SOR3Dta{S^Jl@@H(qtjE8z(N&d=b~tb;-T;q+b@0P82U%O? j`x<|N<$F~82|mY{_=+o?-1jxU#rK?H=h^puXlnTfXs@E5 literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/exception/ResourceNotFoundException.class b/target/classes/com/pachedev/restoreserve/exception/ResourceNotFoundException.class new file mode 100644 index 0000000000000000000000000000000000000000..d1da5fa15898cd44a9a5fc9c329e472982f2bbab GIT binary patch literal 476 zcmbtQO;5r=5Pe&LYDGbe9z2+M^CPi80KtQiQzZrr_obbbHSI3hZV7+OlZgj^fIrGO zMbv}wjj0ou>X$RIMAku#7FVkuNed~yQkNdRHKDVhKP delta 291 zcmZ20vQlJ26eBxZdTDBLN%7=DMhQM%2s=KnG&d==X!1nH>AZQRIXQ_*IjPJHK(JYr z$&QiD9ikzVS)N&lL3naEvp2ISgV^Mo%&VbNo4Z)D8S4cY7#a8(m>77A8F(24JQ)Om zY@m9eC_B(7Zm=vPNLBq@x&QfKsvya(ETWLKJc{ yY_4OgWD6oV{A~_6FdvVaf)Q2mqDR|HRJ&*pC6u8gln_V&tOd(pGTpQ%0D+)6<69^at|%? zN{Fq+_e1X#)TxsmZRtDUWku5UFm!+@J#zrtW z{Y}og^Rx7-_C&yiveJGLUo26#p0LiXa3Ji3_KvWw&k7Tz?8}y@P`JJPi!O0B;>o!- zvc6My*Nr+IDek8ikVqP7+>fJOv{EYuyMN-=%==E|eRnE#q%#+!!e%}z-Be!lT5sDU z+MT!X_;1ZSI@p1-j*TmJWIz+T_@kq&)~B7pL(9}ROr_|wWhiZPZXw`k<3!CCKobFP1LGmb1zLTE-9Y4ZiWB|HfWyi3*QDdSYM*^^hT7lFxW7bF) z3~MSqJaJ9tJb_llqYC}9EZMF}uJZ!tvKvctR`6~VSmBTn*p_po;YopVdknL@XRqb* zSecqVFgl#+*ea!4vMpCCQi0k&J1;df3Y^~fFh$}N($exwZ~jI_l1O5^jvd&^)0{Oe z)9VqaY3~?I;uhShp-JHGjXB4jOvi25MHMZhNDJN8-jS{NXT)<%Ybv9px9GSXtpb`W zy&)fUSNmGbK%qT4_F^A3EgEx|j5&c_tGEtWo}7}7&-M--ZMajQfoumu?B;655pu^^ z1Kx(aG#nH-zVT5bBbJM9I*2^a@XD@^LpV$+y<@!t+1~Tn3j!x%9J&D)U*irPo#+ZQ z9`Tvet8sC)5wpNnM|2!TTA;pQ=M2xZEqapH+QJF5A)&9`aUE~R34uo1GumLnZb`q0 zE~9KNa7srVwl(5z+^gXpfu4m}9ymeV`4rX`2U#R=*7fhN$T3|AO498;CN5LoL?o36l_7>~x+ zOxmi32`q4L42#$dCx<#usl-5t;St5@x~39HDu?C6Q>6x_H*M!H84gQYY@)H zIQb^V?(xn2J{^zZT^c3?Zmk@?*Kv$_r9ct}wl_jzO2eeUjtYv})iDi|;b1Z>47PBA z=2aRFgm6llK~aM`V}(PstHT0gzSfd+bl&Q&6gpmM@R$w<489s8PeZQiNKtrPRdUKY zWj`s8W%^2V`s17*ybA48IzFw2 z3XN!LSWvLf>iC=*3K|FtI=zB^LB|*Ir2rar(^FgPe*u}_{l22(tE!ncQbyFpl6Vqd zSEJK61fE+{1gcUm9CoT8(d-eEPmx1!m^U(8A&E3)zZK|<>t>tjuRfPOm&=?Zi*mr4 zHC>aXzt^&C4l68i4ECmM!7fhNGwBDVrIJ+UX}ub3S5t^loX8soj|n`y##V7fT+ZdB zUwqZ%YL}gy9J0NByKLnzL^~M`-(eTuczW0bk0ic_?`wE&)h!}$oQ@yhhYYz{qfnN^ ztV=f=Ekb`cakTicj_2`$Kugh>k$twsN$iU0P4mt5c!iZKOhkjEv-aRx+^$jPy5FJ&OkN2O@U1RGwt37KwQ)#!PCh^q5@kpJwZO`=_qZG7fH;EVVvWAxg4)`V3&r;KxwP$2H>wB7r#-x!`S1ldC!7I!i zHuMN2@XzWf2q^>R4;fazAl-vm+nyIW)o4FN#S}Dzr*h}ye@EXjj02J1aZ|L8T)y> zA@J1y-@KtW9<6dm)EcWm>zL}d_4pG5cTl=UTh6xH%zT?@v+Oo$@ryy0XW;4s7Ar_! z!EjyaYWSO&ol}lYk__mGj8tRci1HYc(TFW)F zjO6$t^ug|~1*8_xtk&HN*x&s!4lJNueReP4Sm!G^*}a6*3A};iQ0I{aoPCkx37q3| zKN$n9WWEO}-eYgaKHNd*VO~^E;Q)HM+Zl+%dGzs%g7E+@P^EgDKtCQNr&Cn%A->gM zfctfV2ulBpct@!76jyboeM*_GC1exmx{hIB0VC9Eu#<{bY1QOQJj9N97|p!QwxGk; zq%B~wqRBpt<0=KUP=26ED1Y3S&#{hj-_7>~-s9WuUu00n?{i9zCA^m-$TAFCF*iu9 z*y7LBzJT4WwM)1rd~*G8q$*h?QEZP>lp?-m8PAS)zJ~8!$B%$T{G@V_fcs&eyV9T* sKjSljpW_!?)$T93{))dQLSH4r1uS71f8fp^@n^zSq5KQ}hJT>-zgte_j{pDw delta 141 zcmZqDc+SLi>ff$?3=9mm3<49m!sOU&GV?M^>=+rCH8jH*8Cab2Q&PDYm>D>E7+4wD zCJV9VPB!J$3}#Sd04f1$V*t{OASocp4&>>B`3wxKTH6^oHUb5h7`T8WNSXntfg4Ej TfZ41-nit4pWZ(mmObq-0gWeCM diff --git a/target/classes/data.sql b/target/classes/data.sql new file mode 100644 index 0000000..18ec77e --- /dev/null +++ b/target/classes/data.sql @@ -0,0 +1,11 @@ +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 1', 4, 'AVAILABLE', 'SALON'); + +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 2', 2, 'RESERVED', 'TERRACE'); + +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); + +INSERT INTO restaurant_tables (name, max_pax, status, location) +VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); \ No newline at end of file From d959acab795279f7f0aa9b513e9c8be7d57a6b17 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 7 May 2026 18:43:08 +0100 Subject: [PATCH 05/47] feat: complete restaurant table CRUD operations --- .vscode/launch.json | 14 +++++++++ .../controller/RestaurantTableController.java | 18 ++++++++++-- .../service/RestaurantTableService.java | 27 +++++++++++++++--- .../RestaurantTableController.class | Bin 3156 -> 4366 bytes .../service/RestaurantTableService.class | Bin 5122 -> 5827 bytes 5 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d4726ae --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "configurations": [ + { + "type": "java", + "name": "Spring Boot-RestoreserveApplication", + "request": "launch", + "cwd": "${workspaceFolder}", + "mainClass": "com.pachedev.restoreserve.RestoreserveApplication", + "projectName": "restoreserve", + "args": "", + "envFile": "${workspaceFolder}/.env" + } + ] +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java index 28bc52b..5e9ed0b 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -4,9 +4,12 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -42,10 +45,21 @@ public ResponseEntity> findAll() { return ResponseEntity.ok(restaurantTableService.findAll()); } + @PutMapping("/{id}") + public ResponseEntity update(@PathVariable Long id, + @Valid @RequestBody RestaurantTableRequestDTO dto) { + RestaurantTableResponseDTO response = restaurantTableService.update(id, dto); + + return ResponseEntity.ok(response); + } + + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id) { + return ResponseEntity.noContent().build(); + } + /* * TODO - * · update - * · delete * · findByMaxPax * · findByStatus * · findByLocation diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java index a40c71b..862f4b8 100644 --- a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -35,12 +35,17 @@ public RestaurantTableResponseDTO create(RestaurantTableRequestDTO dto) { } public RestaurantTableResponseDTO findById(Long id) { - RestaurantTable table = restaurantTableRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + id + " no encontrada")); + RestaurantTable table = getTableById(id); return toResponseDTO(table); } + private RestaurantTable getTableById(Long id) { + RestaurantTable table = restaurantTableRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + id + " no encontrada")); + return table; + } + public List findAll() { List tables = restaurantTableRepository.findAll(); List response = new ArrayList<>(); @@ -60,10 +65,24 @@ public RestaurantTableResponseDTO toResponseDTO(RestaurantTable table) { table.getLocation()); } + public RestaurantTableResponseDTO update(Long id, RestaurantTableRequestDTO dto) { + RestaurantTable table = getTableById(id); + + table.setName(dto.name()); + table.setMaxPax(dto.maxPax()); + table.setLocation(dto.location()); + + RestaurantTable updatedTable = restaurantTableRepository.save(table); + + return toResponseDTO(updatedTable); + } + + public void delete(Long id) { + restaurantTableRepository.delete(getTableById(id)); + } + /* * TODO - * · update - * · delete * · findByMaxPax * · findByStatus * · findByLocation diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class index 2593801138e8e4abde4148d28f82f74a0c99c951..84842490ccfe2adfa51e5dec764161f2b93520da 100644 GIT binary patch delta 786 zcmZ`%OHUI~6#mX=Z<(1|X?ZA*gdmdCDYXberIjYFEHJbMU-eNR49-|vnwcuCi7R#C z)3JbT=~VZ7|$Yd7Am>CXdg>of zCAVs&GtyBKv00z=S5dr(LIMJ553=wnj$=4$s-9W#3!PwW@WFX&qNk z4&o}|xTIjWbVb+T4o(QyFiQw5doA)y$0M;E!gbt|CH4C#A^iVD41=(Tua*!+0D@0K zX0@fjFjS1l7us`*$*jIY3HeIzdngzM6wE{C80M}#uajlaDyFfIv53Mz$|r?+FYxN* zRc4XB9QPZq5s8nzMX{LJL}r~K1qSaLM_`iU6ykg`+H{N|!5KFqi(HXsd&i?lGF@ZC z<2c5XCm2;hy6AJ*fcsQpC4>gU#Gz$ZK7aU-hm7rV)RG$i|MLee$PnoUnY<_6ff$?3=9mm4B_kyOpFY66C0&AYOUm$Y|T^4=EuX}$>2YEAJ6s4%Xozu zStjq|O<Il*Y&Iaz z2S^J5u^j^w5Hc`uX>Dil)zaR|Ah;38V`9((k|1>q3}Tbl3TWvEfJFm=<}ok`0p){$ cbTClfBAS7*m?4BA6v*cS@>Ln+8N%2&0kYs93;+NC diff --git a/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class b/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class index b33d29bc7e5187f1b1847d796b77fde0d08afcbb..00a3509a09db2e5f40baab517f212051a7a24726 100644 GIT binary patch delta 1654 zcmZWpTU1n482h_N`MU@W*V90022)&gD@ysNqH$N zt#EfSx>%X1UCdkHKq+Qv+5KYAtsZ)4t=8(bRV(}U856QKv(LZxfBp9N|L1HjI8$t$ z|9ki(fO+`wh}C6eTM($VQHOc~yEPnJ8ft0}S8S+h5m+Rr8pZN=%`JaR%ABM{s7edA zh1P{i+Cv?!CBbM%>zowK#BDYfVW~iYT%r5rUc+ZBv#}g^$Pr_9-krG1#0mkQ6W9}r zw3pO(#v;*r7d$7vHy#f=)e9ODKLK}jS?wExzQd1DYY7=b& zo-r3?wb70>a+*8eh+?gYPPx*Z>FPqaT<^|vN6=$q9oEY|JVdd<#zx$K?3`N@`Zj@C z@~}C}vS8s+8{6@i^jZ_AK4D`A(gjR1QWshiR^}&dJcS&AWa?@|y^BM=O51N^7o=Qe zRp&ly<2jTFq|hN4Z4RkhTG)!+HavJip0)P*jh@byP%JF4t>6kK<+PM+c}nC+P0Vkw z6K*X|GX#v5aC=xqO}H{@&g5mP1Fzb68LtVXkGI!1wS}8w^blAeC%Urb_LNcmpuoKT zG{5}IGs$h?ZMJ6?_ z7?r=K{**D2hHf=EAKQ2n`@IIv2$UDdawD6W^OMh7_za($I4=vmlg*>}LST1+jCyjV z-?KpO^qTUFce?(m8(+&mye0Z~0@uq@EW7{?L~g~PZVeHcXJD6HC|v7$cc^ekfWVq{QeV#>JUS(l4v zT`Hc*-2W>s)ZEh$a)2~BATAI>63=A{L>$cm!M+iM8;8(ZOHB6(%+~kApQXpqAwX1h zMS)`S5l2kJa;5GS7`XVegH?A3^7G=rbYi^`KHNl_W}uXEK5lj}2b{Zfc8^DW9t@<-Ssi-M+#-Og4V~{q83?==8 z$W@a6sfxJbIA&;yo!NRIOaITfB-p4VCslkot9h9k0*$Hm$=iNnM&52nOaDUg0JN(mvAdbHKJaH%)uVV9t}sR(#B121K})j zASiz6?m(E!9k&Sx;|7->LcGqzducXU?hQ&s(tO6MXe%&;eX@E&*+#ymv@6>*`U}dd zMsT1pZ~|`)gWM0{_*e&2D)3t6e5y1$XJ74xaDq}<vI!j6vlt=Cf!Z8n>0ylS~|27hZ!hoW7?UHjFd}?H*ng5qC&k?SB$Z=G&QDJ z>!k&+C<^$hC}6!7yrJA|XsZaKsC@T>ADD3*{{a61#j`0h4l{e+v-_OqJn!?Kch9GG zHia(!J9`FL$wdo8x7rQ5ucGnz+IBNk$uhbfZlg!hr2h%k*NqhW2h;0^i~ac_cd#QS zZ5teJ=MH^?wLEbrcU#=0h;4PZyXmW_4x8AlwRMyJ%Z%&atcl=*4)=1O#Y4I;P*>?9 zn`OyaJfe35s(p{rulENQCkEJNF?f~ax?;BA7V`y%A@cfCAQ8y&m_vcbbww}{7+}O< zJ5QWU1r4Qk$az+q&pABL3wp%9w)G{4m$^n^<+8;dcU#uR<`sumX;1_O>vl&sxT7BW zn#1et(HHEsjr$zlAg!nrLRWr^>mA=_f;Sz)yro+zr}PQul6p_?4XtT>pARiQP(;d8 z7<7kn>5aug|4`21BR$3n;G*KdhW7Fl_)81Pz@?6+D-K_P7x(2%@vj)~spX?9AyD6vaox5VBOdVaXo8KH&C zQ|zFI71Yt8r)rXm!n`f$eyN%w{f=Z$c$LU{!gjL6yE+)pjL8onb+6VYd?h(L$D!Wj zX(ng+1WJ7R|22A2$Q621@nrmbUVi(6FC}}`qmsXph)Mk$5u9d*InGJt2YwXX`wf5M IXMSbDf3MiwivR!s From ae8dcf9563d2250dcf4f874cf43e314422ed1ab8 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 7 May 2026 20:31:17 +0100 Subject: [PATCH 06/47] feat: add table capacity filter and fix CRUD endpoints --- .../controller/RestaurantTableController.java | 10 +++++++--- .../repository/RestaurantTableRepository.java | 4 +--- .../service/RestaurantTableService.java | 15 +++++++++++++-- .../RestaurantTableController.class | Bin 4366 -> 4801 bytes .../RestaurantTableRepository.class | Bin 1483 -> 1499 bytes .../service/RestaurantTableService.class | Bin 5827 -> 6565 bytes 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java index 5e9ed0b..fe1883f 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -29,7 +28,7 @@ public class RestaurantTableController { private final RestaurantTableService restaurantTableService; @PostMapping - public ResponseEntity create(@Valid @RequestHeader RestaurantTableRequestDTO dto) { + public ResponseEntity create(@Valid @RequestBody RestaurantTableRequestDTO dto) { RestaurantTableResponseDTO response = restaurantTableService.create(dto); return ResponseEntity.status(HttpStatus.CREATED).body(response); @@ -55,12 +54,17 @@ public ResponseEntity update(@PathVariable Long id, @DeleteMapping("/{id}") public ResponseEntity delete(@PathVariable Long id) { + restaurantTableService.delete(id); return ResponseEntity.noContent().build(); } + @GetMapping("/capacity/{pax}") + public ResponseEntity> findByMaxPax(@PathVariable Integer pax) { + return ResponseEntity.ok(restaurantTableService.findByMaxPax(pax)); + } + /* * TODO - * · findByMaxPax * · findByStatus * · findByLocation * · findByLocationAndStatus diff --git a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java index ed29c2c..0b26595 100644 --- a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java +++ b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java @@ -10,7 +10,7 @@ public interface RestaurantTableRepository extends JpaRepository { - List findByMaxPax(Integer pax); + List findByMaxPaxGreaterThanEqual(Integer pax); List findByStatus(TableStatus status); @@ -18,6 +18,4 @@ public interface RestaurantTableRepository extends JpaRepository findByLocationAndStatus(TableLocation location, TableStatus status); - - } diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java index 862f4b8..6823eda 100644 --- a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -48,12 +48,17 @@ private RestaurantTable getTableById(Long id) { public List findAll() { List tables = restaurantTableRepository.findAll(); + List response = toResponseList(tables); + + return response; + } + + private List toResponseList(List tables) { List response = new ArrayList<>(); for (RestaurantTable r : tables) { response.add(toResponseDTO(r)); } - return response; } @@ -81,9 +86,15 @@ public void delete(Long id) { restaurantTableRepository.delete(getTableById(id)); } + public List findByMaxPax(Integer pax) { + List tables = restaurantTableRepository.findByMaxPaxGreaterThanEqual(pax); + List response = toResponseList(tables); + + return response; + } + /* * TODO - * · findByMaxPax * · findByStatus * · findByLocation * · findByLocationAndStatus diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class index 84842490ccfe2adfa51e5dec764161f2b93520da..86698b729af8eab659fae1119d6bb53991fd5468 100644 GIT binary patch delta 725 zcmZuvO-~b16g{`tmQEl2kWq zjfuXUI};Z!C7@y#ByL^#7yKP2##`Dn)v$PT=iGbGx#zyT*S_z5&yPQi9RQc`$PNo3 z^5(_i4eg4B#w5`J*=JFm^VxDWTQMh#nfm5O%UipmK*fwe6tkO8l%q7P;)XyRHwpd2 zu@!w?59RejE)**ka^aB$1w*(ca2t2zd*?Mr0{88>M>sFZuF9gVUk(cI=?9o1SW{Ia zpCO1PqmY@bPwTa~USmt$qS1nx&1Ki*uYE!F)!qe7rg~?S1a~N{m-Mt@)9VL}fRd9ALk+6KaMLOi!U6j(+vM2& zbb&B0g=cUo%DHB@SKER&@CDbmvG5Id3IEGoKrb#Lz^w;c1dJlch^g|YOFh1bWM_!p z88Xg;CK8|uQu3wC_bmO7Q5#o*%U!CXqbjh39M{ishm{zB!7>c=V1@si)K!iiK9Tkg ziuM(GE$|uM4bHaiqBUZmJwk4IPK+0Ki=|zU`?;;mc#Y*+J}WR8H}igeUsam}36ASF F$6o+Qj4c2F delta 420 zcmYLDOG`pw6rAtcy>@+G#f&153MsBeM3@?Ao1lli>}_V5X=MgM(cTu(F0ezJpl#cd z-Yue*`6pcTGlJ-7ix%IRGjoRd=EHl|p;dlf-vC6gXM`jWEgiR(^$|$~Nr)!uwYjNi zlakk_f-%epoKReypdmw;DyEA@4;Cy~#1fsFV`>6PBUS_k z=t}LPceCAU!77FYj*6y0b|az9K6(5Dt}jw1k>BLE4rFf-a9Fh_lkworsOi52x3 z^DkHyxUJiCo(i9ULHKyO|4i_shZ)ttC0L?;iR delta 24 fcmcc3eVTj1LSCM<%)At*O5enafW(T8`(#-GcK-=O diff --git a/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class b/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class index 00a3509a09db2e5f40baab517f212051a7a24726..5e670504fb4b501823f50ffdab63bdb99596dd7c 100644 GIT binary patch delta 1487 zcmZ8h`*Raj6#nj}$tIgkn>0-j8LS@WVo(0)S++G z1KnmM8W2#hY&yWJ9c8@3F)ZyU<)-_cP>QIjF%jlDZjBs(28a`8j)u-NZvGw%}oIb(92p@u-T& z@Hm6qu6H#WJw((}TDFFPB^g{zrrxqXtS46L>!OB?r>NV-wyi3j!Lz(uxwdMXis#{@ z)H@9Y3b=|FF_XbTSXl2_uJ;J^Wfj}8gP%~A74KBB3jqcv1%%=)x*3Vb6!hUW z6}jjis&KZlssRcY3QWU%62_gzoA=?QvV6Phap=neQ-m}G}?@0kndN|vk|_{ zRabln<1)q=+-a%PQB7tt5^Ga2fy4ZgYk}>EilaEjD|72D!}yeU=WexqiLYdw;&R@N zmap**pPhHX|AiZy#oU}$F@%G8Hhj;pq?9kQ74bpWEH&uH4+?(9uQGn&Nq3p$D9-YJ zcYn@LcKpF>Jpp!}Cp;Sai|_JOGw0A*&pwtiA;Cf;ny($i+%dE;gtYPz7~zWjgj!Hb z|GDXio9Sxiz>RrWLRd*A3w5}ab_(o>po6%%sor{ek`N`_Pd4N0ob<&Alf>Xt1wBPU z65?=>JxPF-?EDENnn#fgmye-~VPVc5Tsl{pl*V5TziBu?QO3xu{9g){rAk$kGN<^Rv9|xGBrK7%HB?QX=zz2O=Q?(Lp9k zbnqmVxeC%{cwv>nWSI1Wj9$@n139gXazHsXSWNMv!Mn&Wku91)Uo(mPboq#e=jh=D zS})OhWfZRp*`+D6*P{TpqY(9cpg^;{jyL$3f+pV`upvYj6QXNc-PSGfYv05XdX0f#iQrLRhPy zacHcZJ`QW(#NF`Vo+-L|Cw28=5Fb$U&g@`F6qy-}E3)2}adwg;F?y_h1aD?6EvJ1fu3{$}>EqN>RG7zaa0Z}wL^g}a8X2nHhaXA{6 zWf!}dUF^j&&C1HOv z&J(&Lzq;W`i@lUcww{F3Zf{BC=%+27p;F-&tj!+ku!nN!K8xqj`bd6Dbic*(#1;7> zXiat7PBPVRvYP`I1-v+FSQ}OKs}_ejqA00togJ>v?(d1;vY|K8?JTdmL(#0O-4%LI z!IbAM#md>wIuop^GC9FJ1}7DPxv)+ro$T+ic$fF|iNfo=BNnF^)qfPOb|2>h-4NL8 zJmy za?QGCn=OzU$gNmfi_oZlFO9hma!414J4;r}AZukQTBL(@M7f!I+VsxwlJb`X_Xq}l z7aihm5>v7>FY628D-MsWUaj}q~A4}vk WpYl0h3-b-%37*@k@A;97l>Gx1Eb|Ef From 95b4609a4d5b4682acbeca06c0fedbd41b70bee2 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 7 May 2026 21:18:04 +0100 Subject: [PATCH 07/47] feat: add restaurant table filtering endpoints --- .../controller/RestaurantTableController.java | 25 +++++++++++++---- .../service/RestaurantTableService.java | 26 ++++++++++++++---- .../RestaurantTableController.class | Bin 4801 -> 6736 bytes .../service/RestaurantTableService.class | Bin 6565 -> 7937 bytes 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java index fe1883f..b0b76d1 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -11,10 +11,13 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.pachedev.restoreserve.dto.RestaurantTableRequestDTO; import com.pachedev.restoreserve.dto.RestaurantTableResponseDTO; +import com.pachedev.restoreserve.model.enums.TableLocation; +import com.pachedev.restoreserve.model.enums.TableStatus; import com.pachedev.restoreserve.service.RestaurantTableService; import jakarta.validation.Valid; @@ -63,10 +66,20 @@ public ResponseEntity> findByMaxPax(@PathVariab return ResponseEntity.ok(restaurantTableService.findByMaxPax(pax)); } - /* - * TODO - * · findByStatus - * · findByLocation - * · findByLocationAndStatus - */ + @GetMapping("/status/{status}") + public ResponseEntity> findByStatus(@PathVariable TableStatus status) { + return ResponseEntity.ok(restaurantTableService.findByStatus(status)); + } + + @GetMapping("/location/{location}") + public ResponseEntity> findByLocation(@PathVariable TableLocation location) { + return ResponseEntity.ok(restaurantTableService.findByLocation(location)); + } + + // '/location-status?location=SALON&status=AVAILABLE' + @GetMapping("/location-status") + public ResponseEntity> findByLocationAndStatus( + @RequestParam TableLocation location, @RequestParam TableStatus status) { + return ResponseEntity.ok(restaurantTableService.findByLocationAndStatus(location, status)); + } } diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java index 6823eda..d1850a2 100644 --- a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -9,6 +9,7 @@ import com.pachedev.restoreserve.dto.RestaurantTableResponseDTO; import com.pachedev.restoreserve.exception.ResourceNotFoundException; import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.TableLocation; import com.pachedev.restoreserve.model.enums.TableStatus; import com.pachedev.restoreserve.repository.RestaurantTableRepository; @@ -93,11 +94,24 @@ public List findByMaxPax(Integer pax) { return response; } - /* - * TODO - * · findByStatus - * · findByLocation - * · findByLocationAndStatus - */ + public List findByStatus(TableStatus status) { + List tables = restaurantTableRepository.findByStatus(status); + List response = toResponseList(tables); + + return response; + } + + public List findByLocation(TableLocation location) { + List tables = restaurantTableRepository.findByLocation(location); + List response = toResponseList(tables); + return response; + } + + public List findByLocationAndStatus(TableLocation location, TableStatus status) { + List tables = restaurantTableRepository.findByLocationAndStatus(location, status); + List response = toResponseList(tables); + + return response; + } } diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class index 86698b729af8eab659fae1119d6bb53991fd5468..6ca2db479b702473ab32ebd223742763392127bb 100644 GIT binary patch delta 1247 zcmbtSTWb?R6#ixp(HhXxetTSc;(h-q`N-A!v6Ya8!( zE%W9t@Il3bH+&F$)F=Nz5r2lDo|)}hq=^w^nLTsnobNmHoiiWfUxtI9f4u(yU=)wH zLt)6Rws)#OBJnDYg7&Glzz5Svf`^hQ_8a* z87eQhPV8q0YKG+0T9#e%AcCqF7f@q3E=iJI%484br}cSVYnbJVmM*sW{T&EHRSz_lFgq5B&b<*Ur znHbVS-bi;eL-bF9M{7lUX1s?AuDf^tZK7ZQ3okp=FXnBlNm&DDIK&z2_rnR42%CzcyLQkEZJN7$xr**&YNDeaf^TRePPRR7I?x` zz8;ui8lMO@9cg%w;h%!f9Y^58C~t+@Y>eVj9tlVHI0yqMn8psWelTQcjZA?VnBh+Y zvA)VXQ0xoS*E=bBI z7JpN=I#6DQkMev9;g%#o{DZV6u#G+nsjm=9B;Vjlhq9AMP|7|l5yd1@3!CCN@fK)H3Cr94uVNmdnGC!H_rky6|sC_Q{t; z5?B=&I2n{CTZqnLRG<7&Tzv8?(GW(R$@XHEtVRr63?`HJimhd}WZ+=1nw&4*$Y>8# zqi4#@kOefKje(IN8%PTPu^j^w5Hc`uX>Di7(bC?^Ah;38V`9((k|1>q3=)&yOKO?t sgGCD%xPi0~P`(gI7Xjrhq8S*A8HyQ7pyH|w@(iVHoD9)Gx{Qqz0Nsfx<^TWy diff --git a/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class b/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class index 5e670504fb4b501823f50ffdab63bdb99596dd7c..e2ed22c5ae746fb13666d3217842d0c27a4d818d 100644 GIT binary patch delta 1093 zcmZva*-sNu6vn^llx_~CQ=kO`sA&jN1T+>GA}SD3qhbsXt}#|cQpEy-CK?ruFUI&{ zjt~9=KKY<1i=g6;iu=CfF1W8xnh?Jkz-VG7GiUvN_uM;o-*J8LsBbp;)i7&Gfn**)qzJ5jd>j01hg! z)eSVd`*pLbmxt>k)lmi0v;u*=C1nmz%Ums;*uR*3OuXA>%)0f;b>)E zdHvc*v{J)mT#@~8<80S7T*nRRjhm`=;+Bl^?6N(?V>=$nX-}s51l{t%^UV9egDoy7 zc%k7e&c)gAT)OpA=PMU_@YarA$?;~{uj8G9y(6SnPnT4^L^`|^tZy9nD8IaU)-MV& zWVh~@ns~dU>2{kP-xQ<|qymat_QWquPJsid@E{FINXJkew8(kP9Dj;76YPOsH{=VJ1Z7SgA|grx%bjk%U47X+xMSS-xgxDH1S^ z+HAN{M$R1e&Lyo}Hu*Ql%wu-G^!Y2Di!cO>p<{_elNJW}2mC49OyTNG4 zH5vTre;CJ2#xBM&toh72Mz*0tMOiSX1r-I>Wa~jzR7~O5O$*dOLoMP_hayIyekF=g z!iWr>Y&$14JkHJ?YCPKrr%@XyQn8w@6;)=#U~vue{k14-Ggxz4$N>q)uH87vhEw!y zj7}S6zLUtd(7J-HI1|inMyQdI#hldW`ue+2EOE)H8+id)GxVd$G`jSo=v)vy zf(z}q3bf*;vEjclxhBR~%8J`&?GEnJ8jIYcz0cQ6#Zv;G;U!+vZL-C*NgGO~q?Cp*A}T^5$uOFW8Vs?BhzN@6I*>SB z40!EzT20zmF74UAXNy@~ME^u}KCj~Cyyty?=lq_FbI!L7H3nw>lqY~ze!8$3Y?PcY zAqOhbBin2I##UKoPhBe3NFp@oxM9$`P#p-ZsP-^M*~LwRz>)mPe6}y&U&wZzINsY| z@G;IUF~b|26Fw%HlFo3uy~G_E3}19U=820LX^7O?pYlw)BJ<{>0B1cIyz+64>x-Sd zG|1IS&Y6;d&`NKBHy+;e(ZvTTR;_kTGHXz*6T^(jeANZVyGp*uVKeLaW>6zjk*Ext z0pq(&o2`ad{+b8laVkj?AVrKNEG11X)ikqQPDT4FR^g;hen*cks>eeECiO&FD~Dpo z-0M|oRA;ly#fE|{%Gspu&15t;(I%JUC3ia!wkf$?Iuhrqb|`Hpes(EmxAyK)Vu$=n zoT=EWYM1mT3+`^b>?cT%%qF{A{9M7#uHf_JQVy8X Date: Fri, 8 May 2026 17:38:04 +0100 Subject: [PATCH 08/47] refactor: use User for reservation and remove Client to avoid redundancy --- .../dto/ReservationRequestDTO.java | 10 ++++++++++ .../dto/ReservationResponseDTO.java | 9 +++++++++ .../restoreserve/model/entity/Client.java | 3 +-- .../model/entity/Reservation.java | 2 +- .../restoreserve/model/entity/User.java | 9 ++++++--- .../dto/ReservationRequestDTO.class | Bin 0 -> 348 bytes .../dto/ReservationResponseDTO.class | Bin 0 -> 351 bytes .../restoreserve/model/entity/Client.class | Bin 1833 -> 1573 bytes .../model/entity/Reservation.class | Bin 3483 -> 3471 bytes .../restoreserve/model/entity/User.class | Bin 2692 -> 2952 bytes 10 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java create mode 100644 src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java create mode 100644 target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class create mode 100644 target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java new file mode 100644 index 0000000..7b9ec1c --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java @@ -0,0 +1,10 @@ +package com.pachedev.restoreserve.dto; + +public class ReservationRequestDTO { + + /* + * TODO - Datos necesarios para crear una reserva (tableId, userId, + * dateTime, guests). + */ + +} diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java new file mode 100644 index 0000000..b0257d2 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java @@ -0,0 +1,9 @@ +package com.pachedev.restoreserve.dto; + +public class ReservationResponseDTO { + + /* + * TODO - Datos que devolvemos (id, tableName, customerName, + * dateTime, status). + */ +} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Client.java b/src/main/java/com/pachedev/restoreserve/model/entity/Client.java index 1790428..086e3aa 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/Client.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Client.java @@ -24,8 +24,7 @@ public class Client { @Column(name = "name", nullable = false, length = 50) private String name; - @Column(name = "telephone", nullable = false, length = 20) - private String telephone; + @Column(name = "email", unique = true, nullable = false, length = 50) private String email; diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java index a16e198..0e8350f 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java @@ -33,7 +33,7 @@ public class Reservation { @ManyToOne @JoinColumn(name = "user_id", nullable = false) - private Client client; + private User user; @ManyToOne @JoinColumn(name = "table_id", nullable = false) diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/User.java b/src/main/java/com/pachedev/restoreserve/model/entity/User.java index bd443e3..e3fbc81 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/User.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/User.java @@ -25,12 +25,15 @@ public class User { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "email", nullable = false, unique = true) - private String email; - @Column(name = "name", nullable = false, length = 50) private String name; + @Column(name = "telephone", nullable = false, length = 20) + private String telephone; + + @Column(name = "email", nullable = false, unique = true) + private String email; + @Column(name = "password", nullable = false) private String password; diff --git a/target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class new file mode 100644 index 0000000000000000000000000000000000000000..c4a2bff9b7de6428fbb9f517f60bd3f4e17fcfe6 GIT binary patch literal 348 zcmbV|zfQwI48}jFNduwKsv{d4FmMB6;?DwNil|ho?w5;2uS#=}Uh-Z{NDMpx4~6hW zj7(jA_GioTXMg{EegU|}Bt=BHG;X6_wOMh=ooaaNoq(H;Rq36Y_qq1g)pLGprTws& zrHBcWweGa4bp4`c#hQ&L3~y|0{T*R&dbT9Q_pam&1MH8GU`QC}w&tgHQ*g7;Ma6_^ z?u@RMy0Jn36=T1$Tf$YogSBfy_Uzim@S_b2j{iO2B6!CM#HqXj5-B(orlM4@f#m!H bd+$<+kO@N@GH@Uq^?4%kP!!=v7-9SiC@)sE literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class new file mode 100644 index 0000000000000000000000000000000000000000..115380abbb9645466eec5e9565c852178ccf4c2d GIT binary patch literal 351 zcmbV|y-veG5QJwPI|dU-l$MGPD7XgE@Y4iQS&0Nu`m?BcHoDcS*7j8`iVc^6FuXI?ga^Xl{9;9jXRhK5100T!U`QC}#`1i>E!Z!0Q8Qth zyHeLH?M>%@n{im1hH#VrC)+I{dvSYT@{{QHq)$ literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Client.class b/target/classes/com/pachedev/restoreserve/model/entity/Client.class index 87a7a8035fff794176e2b3b8ee1a7cd85be18365..022b1bc5499a6cd877019b08bdcec3763fc77eba 100644 GIT binary patch literal 1573 zcmb7DYflqF6ur|2MJQ5AL0-O5S`b$88DEJhXqr?s(ln88)9s`k*qz1Q*(UreO*AHc z@CW##jAwQ?Qky2NAG$O5&bg0s?(MJNKYkL?3wn~H6r*k1bFEWucST$DEMElL!$NBhANZfLiVN^KgJ#IN%b*y)-V_|DXDcOdawimkO$OT7owE@U zsMk0}&c^`!Z^KZD{?f=Y*~@byes%9DLFck{C@mLUnZ4 zSi6j}!sXI|H&pUd7-1N^9ieLwsaUS`RZL`BO=PO6-VmXRb;omC-ih^B0Q8WlVmcR4 z{BoYMG{I=LE|oY4-InkhrZ+>)v$@mczBG2?%;>I!_*VVD=8l3n5IBe=EB!+u5zVx6 zD^D|Y5&8hSZ$vQ8a-K@YnGIqA=F5ZpD^2XqTcNvN`#twjLSr9L5CL()h5nGFo(Y-+ zBB}$ipGzh6OGfE(r8z}u+Rc+icNt9`dZBNN*U~659dGfD*;7Wtr@SBSI|#21U4lqP zBZnRRAp*J%j%M;{N|`~x%E6k0HHp(GFePZ3@^C<$Zsk+!kX*!p-oGN3sQU z7CT4<5WYj(qtdUBn?4?Ck{+W(`km3~H^xmLuS1f)6s5;jgXEw%=X3u~_Xe}i4Vin2 W?neqfh}Lx$DIdaqg#NK`kN*H8V<52r literal 1833 zcmb7D*=`dt6ur*UbeYmLO+z;dWhqIwfwFg#P$?)pC5;kBhDL%A}Q6sv@0HV`?HcqrHE@ekZq4pTX^M%SN#p$U* zkLWR@(IdCzRryPyl^YA~zo8AzsJA^1f` z)4@#z8_&`l@bhRxx3z%r8a@+qhB3MFnHE1{5~CaVYY3sKCH##LKsV_YB0NjF(}!qz zFrs{4qQYQAt9^;q1|!;l9`Q>WZQ-~vEy~L37h3#;S+_{(K%`uVh(yu?hSz@&=p^h< zX()G}Hv7>p4@w`}C!&XKK>wgWePK}g(0UO)EC>3B{pr^Rr4MZz(dPnsJWy!(Z-0KJ dCl}c#d-yX(&w>lu3SQm>D7O*spnp7!-Cw5bIY0ma diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class b/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class index 7174af71ba15e6cabe9e98623cf8d8545a151710..251e2aca58e72336b270dcc7c40c3bce7094710d 100644 GIT binary patch delta 93 zcmbO&-7md?mywgDv^cehkwMpIqJTO_D3EQ<$RIyCfLU~N9OFYKKKAs~5|Ah(gT96) bNDT{!x4DL;fRUHI7$#?+fsoQ*D`p1(n64RF delta 96 zcmeB|o-MtBmywGtIVUqUuY{37fAR-b4K`;mZ?hxgFD8D@^wbgvpOL{(LlarPgr$Iy Um$MilZ#4Nmt0qjh3|lcf0IyyiCjbBd diff --git a/target/classes/com/pachedev/restoreserve/model/entity/User.class b/target/classes/com/pachedev/restoreserve/model/entity/User.class index cc66c183b457d1de7e165be09a4a2583f669a23d..a181a86892230a2f43fa6cea867f6858a86434a0 100644 GIT binary patch literal 2952 zcmb7FZFdt@5T2VROLzJo?w#hYF*q693@$_Hm zIdb&)!5`p{^7zc|O&e%DyL`y*-nq|x?#w(hlYjpG>u(~uM?dB%BWSJd+Irt;_hd&7 zbWi$$i-+_EQn%fXv~=kNW^klGgDp>4LGv$+fuUQ5)75tlU&wYKC}VbDub*txU8j3j z(1pDahi%z1ee=+gn~viKMqs**FX%krFAXm+^uF|b(+{N6mU^v&V=H5~wxuIIBaoez zVTBT~DL>$K_bA~6?rR?PqZ749)kd?{d@d;K7`7C&Fyi!n;F*!zm&V?|nKpIKVq$Qnh(Bsv*py@zbvfp!^XuBXNCvC&D01F-S=PC^+wjw92j&1r7DsaP z(8Y}gN1yA;pa!oO%Bv&$O7JBmc+DA%k$jb0&h%VqspSP@iUD|Y&-+wuF{n4UublJ?R+82C1@QNzNN%WbWfv;w1mO} z(Wa87_?quSaTvX+(K20uOnxlWV);##R$J)Rr!wfdon6C2DgyK${KSDcF~!j%DldFU z1L8Oz+Rh z+L@t!dX}fApmQhFNS)KDMc=}`s3UfM5>zT5jMngwUeL_G8+vW|*kswJdGcLnHbLVj zLp7R^D6XZkO%@d_c5@+!e7XXacvfKga=~Mj$LbtbQ+Tt6Gm_4b1_R1aZ5Q#L!?9T( z#`y!l7izQ!2nzBvq;L!=0)p~94JpNtcMj-0AgELYnU5~Q)jo{TMHwzXwYvoDC49dt z=`y_sk0Rj3ArO^!nr1oPox{%ibQLF75GIHH0p1_tr{i1TovZv#*M7xEhF0-p2qDG_ zo>~l`HTnoEbUj^9AkxzjeUeDDo{s3#M50^ih(1pwx}A>b4&6=2(MCF=`}Ac3(SvkE z4`~Y}{HFYOK%QZ}D{HUl+Hd$8)xQwwUy2b*f3A(Va~=VmLuV0}ZIvD;(d%jH-|xx`h^BWSkr2klm}f70G78oA{x mJR&Mm7Sl+>`(#edsyfZkcR*z5c{Jg9SRP>gJxDQKfA|l%Wctbg literal 2692 zcmb7FYjfK~6g}(sk)(3#Je;PagaR!%E~!ES6i8ob>IQ@Bgv2#NzA5(NsFkIlhnf5< zn4ts12YvuQis7szCvIv+>IX~OyZ4^G_nvdt|NQ&c-vI98hXOJJw>wT>KQTJTvMUF= zD?Q)gL%IX0_noe^bZPshf2u#Ht$?gR>4h;cbjz@N`tH#S+3^K3W|#KH`9{OBdz%8+ z_5%j@W!v=3BTH`Cw&NSV>DZpYBHdpau5aik()CQwmv%?$^)AD1j@{akwsZ|&cH4#( zNV-jWzFPNA6C!wC>+~dys6VMSTlLnD0y)_?OiN&PB3d>$-Y<^JK4o3UV)R-gx$!~Y3;Vv$ z2%dXmY+5xt=tqsAOKzZ=Be49xu5l~!2d({jbBB2v9p4-DQU{ zY-MF+KjwES=C^JS#xOkFu$=yp^HM)!PW5h^qMBm2KGKjwUf@c@v}H5sA4#{RnyDyp zI)>FYTvM%MW7a=5shZUkT|1L2kDFxrwTS8Y*D8p)R+-$JHnK-0ZdBg3LWMg;v|(in zqh}8bQ%?DNrfvEU1STpg?P*NlI}P8UK8sQlJMyWic$!id*d2u?Fn+#k;YCN$ER7wWP>5pR}mq&Lg9Q%vFJ_ z8LlSzR-{c!T!Kae)fI1-`M$!il1JmrA!admhTLo)43z~&Q*WM ztzY?(!N+{6vVl2N_|zf?tf0yj|5)%z0?=wYAUzRiEgjH$BG5)UpwIC|LTPu?0eyw9 z6M#0;0o}&~%IYF(3Ou7|s@{HuTfgyZl(zYhHYF`ZWJH_#iq!m&)*RJNX$m~TRucSb zTKH%hV)$YR|2P?bEiHUBNilpWgx^kv-$)A|O<@dwHH3eHT2gcFriG8jIEG&g;h$nB z34Svzd^G$q{Bj7-+m;HrsGNtisnIT0|G>A^?4M}7!pJF?nGsM%mb0$$eLnxiSV$S{ V(<6g}@LE)tP>bv51m&&O{trY;;3@zB From 9e8fa0ad381d35aa80e4f68d3000f1d5531a169a Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Fri, 8 May 2026 17:50:18 +0100 Subject: [PATCH 09/47] feat: add Reservation DTOs --- .../dto/ReservationRequestDTO.java | 13 ++++--- .../dto/ReservationResponseDTO.java | 16 +++++---- .../restoreserve/model/entity/Client.java | 32 ------------------ .../model/entity/Reservation.java | 4 +-- .../dto/ReservationRequestDTO.class | Bin 348 -> 2857 bytes .../dto/ReservationResponseDTO.class | Bin 351 -> 2261 bytes .../restoreserve/model/entity/Client.class | Bin 1573 -> 0 bytes .../model/entity/Reservation.class | Bin 3471 -> 3476 bytes 8 files changed, 20 insertions(+), 45 deletions(-) delete mode 100644 src/main/java/com/pachedev/restoreserve/model/entity/Client.java delete mode 100644 target/classes/com/pachedev/restoreserve/model/entity/Client.class diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java index 7b9ec1c..d7aaeb8 100644 --- a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java @@ -1,10 +1,13 @@ package com.pachedev.restoreserve.dto; -public class ReservationRequestDTO { +import java.time.LocalDateTime; - /* - * TODO - Datos necesarios para crear una reserva (tableId, userId, - * dateTime, guests). - */ +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +public record ReservationRequestDTO( + @NotNull(message = "el id del usuario es obligatorio") Long userId, + @NotNull(message = "el id de la mesa es obligatorio") Long tableId, + @NotNull(message = "la fecha de reserva es obligatoria") LocalDateTime reservationDate, + @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests) { } diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java index b0257d2..5eab010 100644 --- a/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java @@ -1,9 +1,13 @@ package com.pachedev.restoreserve.dto; -public class ReservationResponseDTO { +import com.pachedev.restoreserve.model.enums.ReservationStatus; - /* - * TODO - Datos que devolvemos (id, tableName, customerName, - * dateTime, status). - */ -} +public record ReservationResponseDTO( + Long id, + String tableName, + String customerName, + String dateTime, + Integer numberOfGuests, + ReservationStatus status) { + +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Client.java b/src/main/java/com/pachedev/restoreserve/model/entity/Client.java deleted file mode 100644 index 086e3aa..0000000 --- a/src/main/java/com/pachedev/restoreserve/model/entity/Client.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.pachedev.restoreserve.model.entity; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Entity -@Table(name = "clients") -@Getter -@Setter -@NoArgsConstructor -public class Client { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(name = "name", nullable = false, length = 50) - private String name; - - - - @Column(name = "email", unique = true, nullable = false, length = 50) - private String email; - -} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java index 0e8350f..284c36a 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java @@ -39,11 +39,11 @@ public class Reservation { @JoinColumn(name = "table_id", nullable = false) private RestaurantTable table; - @Column(name = "reservation_data") + @Column(name = "reservation_date", nullable = false) private LocalDateTime reservationDate; @CreationTimestamp - @Column(name = "created_At") + @Column(name = "created_at") private LocalDateTime createdAt; @Column(name = "number_of_guests") diff --git a/target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class index c4a2bff9b7de6428fbb9f517f60bd3f4e17fcfe6..6f57d293ec4bf7e34a809e1094073e59144ecde5 100644 GIT binary patch literal 2857 zcmbVOTTdHT5dIFaF*s{5=Ar^6xD*PQ5SONxG)~hdkc+8-6c7*X)4@J58`isYcWtTv zsg)Y3s1gr-$y!UM zfEQa}SxaW=LiS;eH(3c{+mSv4m~AGhlggKzvb41Lm^+5KM3~pAd?p!RHni-9lmcUR zy>_bHgV!%KujFAeLR-=HRaLpfT4ikZyQ`XV9gW8OQnn{8Cfl3u@`rfbGi?IK$#PxA zSp@nc$kQ_`TYLA1ZPo^O*b>OBtw$mYFEqC8RMaR0J~j}?u)ui9wAFr)(y`{1b^$FX zyc@j_gxVl$z|aQsE!Y7RYQ0YljA2}0x}_HsTZm`97O#F*Bvp4><8RiM6q>I>nH4o z?oNCqU0GAUawB6PiCcJR;A^l#NhXcbd&)m^Ds5#ll00(iZdpAyb$QJV98UK%Wi)>3 zIKCbr7flo|i6_`j;3=EEL#o>xO8F+ap{9WyJQEl_Z6OG}jWS}|4d-0to7yj>U12XS zmmKH3eo=_zA67|35*q3`w4=PTYhG|rMNPIX>3LB>i1AB5NYr~*afWp=K8znm@KRv2 zIg|_GtSj`)u|oF{V!O@rS>&_CXGOp$+O~3o$ds4B0fim@uAw4t}_>kkMlWLr8FUo6-?JqaKpn04az`bVM0FQTlaL{kkdQa;o&}ru%i%)a85$ zb!YhwW0=GgH`BYA(Gh(VST10w6VW7>-74BLj#=jRzI>)4`OGoJ6&Waed-*fBCNYiNcsGO#%3r=)T* zFf(vY4r0>b0t%F5WEL|rm`*;=Y&v;2i&ij$A_Gt{&>#jN&B(yYzy>7QfjoUMpMil@ nYdZtSMxX!_0~e44NizU7a05vmFq;)f^8$H{417S6iGd#g=kyi! diff --git a/target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class index 115380abbb9645466eec5e9565c852178ccf4c2d..2cb4cf8fe6b1dc27c25058e1a20dde98e823afae 100644 GIT binary patch literal 2261 zcmbVM+fo}x5IrM6D~ZK`YzV?3Cbq%SVteDnN$jk1GsL!R8B&4bA$eM)39K#cD!U`9 z@*(+)Jj7K|rHY4qKt3v!vlqdj5~>OhJw2B`eVgw7=iigR0ep!a137_*&7fm<-R6;O z$zwZ|kqWrT@L1X{71%!{dslgZ&uusGBe}Il4yB#Y8dPT{lreL15t=Xb@g3`+U0AsbK0kU1;u2EDK; zpLucFULW1u_v7{@`e_iTNQG`URTGu)1)dmSpWHZW@UZ7A_NkrbKa(^$U6`lh!9at+ zJ3VKfe;mA(b}IeC^;>Nj-EIWI+g{fho45?P_iI;f$*386T}~?mZ7BCDF8fYDN zGCMk`3j98lXdSeHyBVlTOF2pwtc?@g8;25{5f3OkN9kwe2Kl=67e2N&`k1%w_OWQ) z>to5PO!l#CRi^q_u`2mKR^MId$)|j&3yMCO(v!sHLUopOlrFT*An}W|NthSz^b?z^tJe z#>l|poS%})#lXzKIoXO)kAtNoBeR&1!EE9K>4{oWjMkHXGiwJkC^7(*1I=Ur(u@qO z3~WG>9mvxM^BEXewYD>GYy=80F>nD%kTe5O12>T50kc_wG%t|H$iN3AnHcy1)AJb{ diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Client.class b/target/classes/com/pachedev/restoreserve/model/entity/Client.class deleted file mode 100644 index 022b1bc5499a6cd877019b08bdcec3763fc77eba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1573 zcmb7DYflqF6ur|2MJQ5AL0-O5S`b$88DEJhXqr?s(ln88)9s`k*qz1Q*(UreO*AHc z@CW##jAwQ?Qky2NAG$O5&bg0s?(MJNKYkL?3wn~H6r*k1bFEWucST$DEMElL!$NBhANZfLiVN^KgJ#IN%b*y)-V_|DXDcOdawimkO$OT7owE@U zsMk0}&c^`!Z^KZD{?f=Y*~@byes%9DLFck{C@mLUnZ4 zSi6j}!sXI|H&pUd7-1N^9ieLwsaUS`RZL`BO=PO6-VmXRb;omC-ih^B0Q8WlVmcR4 z{BoYMG{I=LE|oY4-InkhrZ+>)v$@mczBG2?%;>I!_*VVD=8l3n5IBe=EB!+u5zVx6 zD^D|Y5&8hSZ$vQ8a-K@YnGIqA=F5ZpD^2XqTcNvN`#twjLSr9L5CL()h5nGFo(Y-+ zBB}$ipGzh6OGfE(r8z}u+Rc+icNt9`dZBNN*U~659dGfD*;7Wtr@SBSI|#21U4lqP zBZnRRAp*J%j%M;{N|`~x%E6k0HHp(GFePZ3@^C<$Zsk+!kX*!p-oGN3sQU z7CT4<5WYj(qtdUBn?4?Ck{+W(`km3~H^xmLuS1f)6s5;jgXEw%=X3u~_Xe}i4Vin2 W?neqfh}Lx$DIdaqg#NK`kN*H8V<52r diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class b/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class index 251e2aca58e72336b270dcc7c40c3bce7094710d..5be81a389ca3bba38dcecdb6339d2b1c99181214 100644 GIT binary patch delta 43 ycmeB|o+7;=l!-BQau}04W8&s{Of9T}0t}1{QVdKCg2fEd48l Date: Sat, 9 May 2026 10:41:16 +0100 Subject: [PATCH 10/47] feat: add reservation creation logic --- .../controller/ReservationController.java | 39 +++++++ .../controller/RestaurantTableController.java | 2 +- .../dto/ReservationResponseDTO.java | 7 +- .../exception/BusinessLogicException.java | 8 ++ .../exception/GlobalExceptionHandler.java | 10 ++ .../repository/ReservationRepository.java | 17 +++ .../repository/UserRepository.java | 8 ++ .../service/ReservationService.java | 108 ++++++++++++++++++ src/main/resources/data.sql | 18 ++- .../controller/ReservationController.class | Bin 0 -> 2598 bytes .../RestaurantTableController.class | Bin 6736 -> 6737 bytes .../dto/ReservationResponseDTO.class | Bin 2261 -> 2464 bytes .../exception/BusinessLogicException.class | Bin 0 -> 467 bytes .../exception/GlobalExceptionHandler.class | Bin 3893 -> 4506 bytes .../repository/ReservationRepository.class | Bin 0 -> 1154 bytes .../repository/UserRepository.class | Bin 0 -> 366 bytes .../service/ReservationService.class | Bin 0 -> 8246 bytes target/classes/data.sql | 18 ++- 18 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/controller/ReservationController.java create mode 100644 src/main/java/com/pachedev/restoreserve/exception/BusinessLogicException.java create mode 100644 src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java create mode 100644 src/main/java/com/pachedev/restoreserve/repository/UserRepository.java create mode 100644 src/main/java/com/pachedev/restoreserve/service/ReservationService.java create mode 100644 target/classes/com/pachedev/restoreserve/controller/ReservationController.class create mode 100644 target/classes/com/pachedev/restoreserve/exception/BusinessLogicException.class create mode 100644 target/classes/com/pachedev/restoreserve/repository/ReservationRepository.class create mode 100644 target/classes/com/pachedev/restoreserve/repository/UserRepository.class create mode 100644 target/classes/com/pachedev/restoreserve/service/ReservationService.class diff --git a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java new file mode 100644 index 0000000..f6181cd --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java @@ -0,0 +1,39 @@ +package com.pachedev.restoreserve.controller; + +import java.util.List; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.pachedev.restoreserve.dto.ReservationRequestDTO; +import com.pachedev.restoreserve.dto.ReservationResponseDTO; +import com.pachedev.restoreserve.service.ReservationService; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.GetMapping; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/reservations") +public class ReservationController { + + private final ReservationService reservationService; + + @PostMapping + public ResponseEntity create(@Valid @RequestBody ReservationRequestDTO dto) { + ReservationResponseDTO response = reservationService.create(dto); + + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + @GetMapping + public ResponseEntity> findAll() { + return ResponseEntity.ok(reservationService.findAll()); + } + +} diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java index b0b76d1..0532ab9 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -25,7 +25,7 @@ @RestController @RequiredArgsConstructor -@RequestMapping("api/v1/tables") +@RequestMapping("/api/v1/tables") public class RestaurantTableController { private final RestaurantTableService restaurantTableService; diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java index 5eab010..b1278eb 100644 --- a/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationResponseDTO.java @@ -1,12 +1,15 @@ package com.pachedev.restoreserve.dto; +import java.time.LocalDateTime; + import com.pachedev.restoreserve.model.enums.ReservationStatus; public record ReservationResponseDTO( Long id, - String tableName, String customerName, - String dateTime, + String tableName, + LocalDateTime dateTime, + LocalDateTime createdAt, Integer numberOfGuests, ReservationStatus status) { diff --git a/src/main/java/com/pachedev/restoreserve/exception/BusinessLogicException.java b/src/main/java/com/pachedev/restoreserve/exception/BusinessLogicException.java new file mode 100644 index 0000000..2816d30 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/exception/BusinessLogicException.java @@ -0,0 +1,8 @@ +package com.pachedev.restoreserve.exception; + +public class BusinessLogicException extends RuntimeException { + + public BusinessLogicException(String message) { + super(message); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java index d458119..14ee50a 100644 --- a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java @@ -33,4 +33,14 @@ public ResponseEntity> handleValidation(MethodArgumentNotVal return ResponseEntity.badRequest().body(errors); } + + /** + * Maneja excepciones relacionadas con la lógica de negocio. + */ + @ExceptionHandler(BusinessLogicException.class) + public ResponseEntity handleBusinesLogic(BusinessLogicException ex) { + ErrorResponse error = new ErrorResponse("BUSINESS_LOGIC_ERROR", ex.getMessage(), LocalDateTime.now()); + + return new ResponseEntity<>(error, HttpStatus.CONFLICT); + } } \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java b/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java new file mode 100644 index 0000000..6b8736b --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java @@ -0,0 +1,17 @@ +package com.pachedev.restoreserve.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.pachedev.restoreserve.model.entity.Reservation; +import com.pachedev.restoreserve.model.enums.ReservationStatus; + +public interface ReservationRepository extends JpaRepository { + + List findByUserId(Long userId); + + List findByStatus(ReservationStatus status); + + List findByTableIdAndStatus(Long tableId, ReservationStatus status); +} diff --git a/src/main/java/com/pachedev/restoreserve/repository/UserRepository.java b/src/main/java/com/pachedev/restoreserve/repository/UserRepository.java new file mode 100644 index 0000000..539835e --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/repository/UserRepository.java @@ -0,0 +1,8 @@ +package com.pachedev.restoreserve.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import com.pachedev.restoreserve.model.entity.User; + +public interface UserRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java new file mode 100644 index 0000000..4aa5607 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -0,0 +1,108 @@ +package com.pachedev.restoreserve.service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.pachedev.restoreserve.dto.ReservationRequestDTO; +import com.pachedev.restoreserve.dto.ReservationResponseDTO; +import com.pachedev.restoreserve.exception.BusinessLogicException; +import com.pachedev.restoreserve.exception.ResourceNotFoundException; +import com.pachedev.restoreserve.model.entity.Reservation; +import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.entity.User; +import com.pachedev.restoreserve.model.enums.ReservationStatus; +import com.pachedev.restoreserve.repository.ReservationRepository; +import com.pachedev.restoreserve.repository.RestaurantTableRepository; +import com.pachedev.restoreserve.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ReservationService { + + private final ReservationRepository reservationRepository; + private final RestaurantTableRepository tableRepository; + private final UserRepository userRepository; + + public ReservationResponseDTO create(ReservationRequestDTO dto) { + + User user = userRepository.findById(dto.userId()) + .orElseThrow(() -> new ResourceNotFoundException("Usuario con id " + dto.userId() + " no encontrado")); + + RestaurantTable table = tableRepository.findById(dto.tableId()) + .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + dto.tableId() + " no encontrada")); + + if (dto.numberOfGuests() > table.getMaxPax()) { + throw new BusinessLogicException("El número de comensales introducido supera la capacidad de la mesa"); + } + + Reservation conflicReservation = findConflictingReservation(dto.tableId(), dto.reservationDate()); + if (conflicReservation != null) { + LocalDateTime start = conflicReservation.getReservationDate(); + LocalDateTime end = start.plusHours(2); + throw new BusinessLogicException("La mesa con id " + dto.tableId() + " está reservada de " + + start.toLocalTime() + " a " + end.toLocalTime() + + ". Seleccione otra hora."); + } + + Reservation reservation = new Reservation(); + reservation.setUser(user); + reservation.setTable(table); + reservation.setReservationDate(dto.reservationDate()); + reservation.setNumberOfGuests(dto.numberOfGuests()); + reservation.setStatus(ReservationStatus.CONFIRMED); + + Reservation savedReservation = reservationRepository.save(reservation); + + return toResponseDTO(savedReservation); + } + + private ReservationResponseDTO toResponseDTO(Reservation reservation) { + return new ReservationResponseDTO(reservation.getId(), reservation.getUser().getName(), + reservation.getTable().getName(), reservation.getReservationDate(), reservation.getCreatedAt(), + reservation.getNumberOfGuests(), reservation.getStatus()); + + } + + private Reservation findConflictingReservation(Long tableId, LocalDateTime requestedReservationTime) { + List confirmedReservations = reservationRepository.findByTableIdAndStatus(tableId, + ReservationStatus.CONFIRMED); + + LocalDateTime requestedReservationEnd = requestedReservationTime.plusHours(2); + + for (Reservation reservation : confirmedReservations) { + LocalDateTime confirmedReservationTime = reservation.getReservationDate(); + LocalDateTime confirmedReservationEnd = confirmedReservationTime.plusHours(2); + + if (requestedReservationTime.isBefore(confirmedReservationEnd) + && requestedReservationEnd.isAfter(confirmedReservationTime)) { + return reservation; + } + } + + return null; + } + + public List findAll() { + List reservations = reservationRepository.findAll(); + List response = toResponseList(reservations); + + return response; + } + + private List toResponseList(List reservations) { + List response = new ArrayList<>(); + + for (Reservation reservation : reservations) { + response.add(toResponseDTO(reservation)); + } + + return response; + + } + +} diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 18ec77e..5076487 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -8,4 +8,20 @@ INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); INSERT INTO restaurant_tables (name, max_pax, status, location) -VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); \ No newline at end of file +VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); + +INSERT INTO users (name, telephone, email, password, role, active) +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','1234','ROLE_USER',true +); + +INSERT INTO users (name, telephone, email, password, role, active) +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','Admin1234','ROLE_ADMIN',true +); + +INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) +VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED' +); + +INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) +VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED' +); \ No newline at end of file diff --git a/target/classes/com/pachedev/restoreserve/controller/ReservationController.class b/target/classes/com/pachedev/restoreserve/controller/ReservationController.class new file mode 100644 index 0000000000000000000000000000000000000000..ccaaf8023e70d3274b04e4a7f9bf6f40d586a2b1 GIT binary patch literal 2598 zcmcImZBrXn6n<_3Y!g;Xu|-S8zJU!;yVgQ&8=49!rD(7nU>Ilo;!Sc33!B}zdpDi_ z6Mu#}TB{%Y0sbh*=Wb#`G)Y5e`eFCJ?0I?4Jaud6 zs;c3HD$)U8Dr_jH68Jg{JWqwrcCsb48~DZcu7weSi9^|tjwk(n=SlfcRkXll(?BA< zLrZQ&3EVFAcVygA=(v}bUS43V5-O=_;k(=b6IC4?SKn6O#|-7+?vwm{DG2wSs2;lh z{Iu6{vZnH_iEmpTsV?uWPRZsjD=G;x}63WRxnJ0{|q z+H)hf?5S1X4|H!8(B_qHn@^=VDy_mU zc_c$Eord(>YU0z`GgDd^-g7HEYeCf<`baUTstm{*8>1M@;w+{toD*1j6UNxE@V-Fp zO|#eE=l>|yNlFIialyj0z}3EFdrZddFimk}<3r2{SjFx2)!p@n0&_icmQb{#mCxcL zE?c-HaILSCZbsXVestWVtn8P&-9}C>->!=&z zadjIx%##mgS`@gSBiXuJ%kZd3tJ5XdS6|1qvI=)4tAzY21r_P-$lr)3 zs^p3Mi(u0n9KzN0u?Je~zlU}WDL$y1bX(xAWEa4srK;Q|d z+IW(pTCsT>l6a}$h{dwv`mQbr+#S?G2HaTldjc0)$=LK8JzDf+$qQ=b;KJ_9UtaUpx23 zC~tt6(9Sxt?mCT|=GWn0h~$>N=`*&XC!s*e)PlT_H?`Z1ktehU%dOMJz3 Mmg_m5Q)w*o510=UNdN!< literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class index 6ca2db479b702473ab32ebd223742763392127bb..2797cfa76fcbc1afc95fa6fb4adc6896425fa5e9 100644 GIT binary patch delta 15 Wcmca$a?xZ%vj`KP{^k~uGpqnIdIhWi delta 14 Vcmca;a=~Onvj`*a=2nq2tN<gfy*{Q><^ot~9!SUfThJnV;d@40vP?%A{Y_rKGB0DOm^3}giEHT_Pt>okvL zOP*8%8Ad)U8Jx&!EAp$abT*fTIGF#pCmajI>{JEB$74_X2lm(_gz ze4_4qN49|3?9p#W2D?s23KTVq{U~r_n_T1^wq;_FZ8?!_xXQMa*hc)Q*8Qf_-Wg2M zZLS$eezhJ)0&`xkb0~woU!V54hfrWa3sUnUc_afuro+gIdYryrAHVUA-;(XBq{mQC zzE4;J^u7^*tE8e!{@Y&H9+%iSO#@)#_paQLVKZ>MR7V}M-FCuI9~d0hFPhtw=obh+ zpju%X53_h8u;sRFT`%o3VYG*`XrBq6eNp!8MBWONKb9qyWtJ6|RhBiDb*gdA^JEY& zU4{l;aqv9BDXSgk%D`)C%;lz}b&^5JiBjA^T_gRlZmPR2t4U$WR zERRC+8YGVoQlhMkLS{6GnF?7Qg_s&-79XcXSsR7SYLEhMra;z5A#-PtYZUEa#%?jI zD#?a*iZUSx&&Q?)p z)A}1?MK9qo0loUi9w&a4>Zm|`jT z*n$r!_=JLwB>2DqldxH5@PL0#z!G=7Lpy;V@D$Hb!*jggqF!Q`vw4p1;YaMFga*Gi V=`~?~!uy2v3Fi~WCw!0D{y$T>Gw1*S literal 2261 zcmbVM+fo}x5IrM6D~ZK`YzV?3Cbq%SVteDnN$jk1GsL!R8B&4bA$eM)39K#cD!U`9 z@*(+)Jj7K|rHY4qKt3v!vlqdj5~>OhJw2B`eVgw7=iigR0ep!a137_*&7fm<-R6;O z$zwZ|kqWrT@L1X{71%!{dslgZ&uusGBe}Il4yB#Y8dPT{lreL15t=Xb@g3`+U0AsbK0kU1;u2EDK; zpLucFULW1u_v7{@`e_iTNQG`URTGu)1)dmSpWHZW@UZ7A_NkrbKa(^$U6`lh!9at+ zJ3VKfe;mA(b}IeC^;>Nj-EIWI+g{fho45?P_iI;f$*386T}~?mZ7BCDF8fYDN zGCMk`3j98lXdSeHyBVlTOF2pwtc?@g8;25{5f3OkN9kwe2Kl=67e2N&`k1%w_OWQ) z>to5PO!l#CRi^q_u`2mKR^MId$)|j&+^+{js7r2T_2p>b{!4zPrk zHkxP=I*|}vSNVkPotjLU(2Gp0rekGwjTg;^%XCQ?ME_UcC-}K|Qt1cDFL{LS6}!wN zH_ED<9a|v`My9ecU+P-f?%&^XT(80&NNm-Ioj>+Y8*rnjo3SIy$ncD|M3u)QdMHdg#3at}S^s literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class b/target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class index 5219c4ac74f73170b642974a7e71d2887a76e37f..7560b32e1f6b1c9645bb4d9c9eda71f1c1ebc1db 100644 GIT binary patch delta 657 zcmZvZTTc@~6vzJq+bz4@g|;>XO^Qv3MGB!NcnKjsNG+sEOOqBK8Xuaq9m>*mTi6!y zf>*p>>v)eJqG}YC#1G&T@4omIOnf$;ExwrWaAwZ=opb(kX6EDIn<4f4pReBl9LMW- zYE$VCE*f^(;?qsXw7HY6Rm>7YMKBiC|FIn?+H69aVtBi2s@iHtC;zKYu9bMbVb<)a z9#j}Y)2H&Oxnw?HOlN0PiDELB%jQ&=2z#;A(uYTPZ&ok{OT$^j8F~`gxtVk-F)!AY zjJyoTixsi0jHepdPuU7@%$VFNt60XWhI1|q5Io5jjHcDdaK|w!+=uhH=*0yFeaTog z;?0I>#WO}-!zHYXS@pMk6Suv%B~JK;yYJwxsQP}g4UyLNh3>0hc%;Ebtxv{7hVg@G z5s*TntqctK@q|H3**0HESccPh?T&~_ zt4d-(3;}vQA9<0TW@ykUA{k7OFgr#C8V=(Kl^-SSVZ`BA-5b&X?!lAP>1y@G=*&7< z^9$C*c5ftk8SeNJu2QxO*E*xiM6OeOgG?j;9(6y!W3=#;NRa9Tp5rC-&&a4+1c0| zqlp%NfSrxC{)3%~@nA8Tb7tmDa&EdF!^Ye1%PW9kTxoDI3`(YVUw<^+Pb55Y$~&nF zWHn^utv8ljKpR6{RygB1VWo8leL|X$dxj+wf%dTyY|1Eb$aSHNAhAkHg?N>~Y2KmhJsjc)$D{-)cY-rqpanCC I;8OMc0p$8j82|tP diff --git a/target/classes/com/pachedev/restoreserve/repository/ReservationRepository.class b/target/classes/com/pachedev/restoreserve/repository/ReservationRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..f9dfe31f3081cfd5196d82d3670a474f7c468155 GIT binary patch literal 1154 zcmcIkO;5r=5S;~~f~fd~#-k^^xF;`GN%UZX)@VQz6Hg0W%3|AXwp$EGe}VDuc<=}K zql`n#ml2~OdgwMYFY{*JbYI`zUtR&=2(}DJ5jbc{j|QyS<~HwA#X~Le;Ht|p2xKTQ z?$KJDVp>SQHp&_>N1)hYT}EBzJM^;A;Z1EomcYJL4h;h(e5a+D#~-A+r#91!b^ c5Guyd3apNxwHV5!U>%DQMqnd8O9R@?4>&D(Pyhe` literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/repository/UserRepository.class b/target/classes/com/pachedev/restoreserve/repository/UserRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..6b2050bb3ef1381cfdec0f1c2e6d96f38df6b14c GIT binary patch literal 366 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 L8gZI99g+V5VLGSz literal 0 HcmV?d00001 diff --git a/target/classes/data.sql b/target/classes/data.sql index 18ec77e..5076487 100644 --- a/target/classes/data.sql +++ b/target/classes/data.sql @@ -8,4 +8,20 @@ INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); INSERT INTO restaurant_tables (name, max_pax, status, location) -VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); \ No newline at end of file +VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); + +INSERT INTO users (name, telephone, email, password, role, active) +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','1234','ROLE_USER',true +); + +INSERT INTO users (name, telephone, email, password, role, active) +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','Admin1234','ROLE_ADMIN',true +); + +INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) +VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED' +); + +INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) +VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED' +); \ No newline at end of file From 2147630791f3069dca258668167070b4f95d6d46 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Sat, 9 May 2026 14:06:08 +0100 Subject: [PATCH 11/47] feat: add reservation listing and cancellation --- .postman/resources.yaml | 9 ++++ .../.resources/definition.yaml | 2 + ...s a Reservation (soft delete).request.yaml | 5 ++ .../Creates a new reservation-1.request.yaml | 15 ++++++ .../Gets a reservation by its id.request.yaml | 5 ++ .../Gets all reservations.request.yaml | 5 ++ ...s reservation list by User Id.request.yaml | 5 ++ .../Gets reservations by status.request.yaml | 5 ++ postman/globals/workspace.globals.yaml | 2 + .../controller/ReservationController.java | 24 ++++++++++ .../service/ReservationService.java | 45 ++++++++++++++++-- .../controller/ReservationController.class | Bin 2598 -> 4554 bytes .../service/ReservationService.class | Bin 8246 -> 9946 bytes 13 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 .postman/resources.yaml create mode 100644 postman/collections/RestoReserve Collection/.resources/definition.yaml create mode 100644 postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml create mode 100644 postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Gets all reservations.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml create mode 100644 postman/globals/workspace.globals.yaml diff --git a/.postman/resources.yaml b/.postman/resources.yaml new file mode 100644 index 0000000..ed24fef --- /dev/null +++ b/.postman/resources.yaml @@ -0,0 +1,9 @@ +# Use this workspace to collaborate +workspace: + id: 13ec3ef0-3bb0-47ac-b761-570cdb795e02 + +# All resources in the `postman/` folder are automatically registered in Local View. +# Point to additional files outside the `postman/` folder to register them individually. Example: +#localResources: +# collections: +# - ../tests/E2E Test Collection/ diff --git a/postman/collections/RestoReserve Collection/.resources/definition.yaml b/postman/collections/RestoReserve Collection/.resources/definition.yaml new file mode 100644 index 0000000..4420271 --- /dev/null +++ b/postman/collections/RestoReserve Collection/.resources/definition.yaml @@ -0,0 +1,2 @@ +$kind: collection +name: RestoReserve Collection diff --git a/postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml b/postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml new file mode 100644 index 0000000..1e59e52 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml @@ -0,0 +1,5 @@ +$kind: http-request +name: Cancels a Reservation (soft delete) +url: http://localhost:8080/api/v1/reservations/1 +method: DELETE +order: 4441617885660404 diff --git a/postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml b/postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml new file mode 100644 index 0000000..1a55640 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml @@ -0,0 +1,15 @@ +$kind: http-request +name: Creates a new reservation +description: Get all reservations +url: http://localhost:8080/api/v1/reservations +method: POST +body: + type: json + content: |- + { + "userId": 1, + "tableId": 3, + "reservationDate": "2026-05-10T22:00:00", + "numberOfGuests": 4 + } +order: 178851836358 diff --git a/postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml b/postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml new file mode 100644 index 0000000..da526fd --- /dev/null +++ b/postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml @@ -0,0 +1,5 @@ +$kind: http-request +name: Gets a reservation by its id +url: http://localhost:8080/api/v1/reservations/1 +method: GET +order: 1442191939044361 diff --git a/postman/collections/RestoReserve Collection/Gets all reservations.request.yaml b/postman/collections/RestoReserve Collection/Gets all reservations.request.yaml new file mode 100644 index 0000000..ad1bc70 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Gets all reservations.request.yaml @@ -0,0 +1,5 @@ +$kind: http-request +name: Gets all reservations +url: http://localhost:8080/api/v1/reservations +method: GET +order: 228234296794 diff --git a/postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml b/postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml new file mode 100644 index 0000000..18ef6b2 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml @@ -0,0 +1,5 @@ +$kind: http-request +name: Gets reservation list by User Id +url: http://localhost:8080/api/v1/reservations/user/1 +method: GET +order: 3809538882598051 diff --git a/postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml b/postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml new file mode 100644 index 0000000..96b1389 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml @@ -0,0 +1,5 @@ +$kind: http-request +name: Gets reservations by status +url: http://localhost:8080/api/v1/reservations/status/CONFIRMED +method: GET +order: 4411268052689732 diff --git a/postman/globals/workspace.globals.yaml b/postman/globals/workspace.globals.yaml new file mode 100644 index 0000000..e96c6d6 --- /dev/null +++ b/postman/globals/workspace.globals.yaml @@ -0,0 +1,2 @@ +name: Globals +values: [] diff --git a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java index f6181cd..256659d 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java @@ -10,12 +10,15 @@ import com.pachedev.restoreserve.dto.ReservationRequestDTO; import com.pachedev.restoreserve.dto.ReservationResponseDTO; +import com.pachedev.restoreserve.model.enums.ReservationStatus; import com.pachedev.restoreserve.service.ReservationService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; @RestController @RequiredArgsConstructor @@ -36,4 +39,25 @@ public ResponseEntity> findAll() { return ResponseEntity.ok(reservationService.findAll()); } + @GetMapping("/{id}") + public ResponseEntity findById(@PathVariable Long id) { + return ResponseEntity.ok(reservationService.findById(id)); + } + + @GetMapping("/user/{userId}") + public ResponseEntity> findByUserId(@PathVariable Long userId) { + return ResponseEntity.ok(reservationService.findByUserId(userId)); + } + + @GetMapping("/status/{status}") + public ResponseEntity> findByStatus(@PathVariable ReservationStatus status) { + return ResponseEntity.ok(reservationService.findByStatus(status)); + } + + @DeleteMapping("/{id}") + public ResponseEntity cancelReservation(@PathVariable Long id) { + reservationService.cancelReservation(id); + + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 4aa5607..7528b5a 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -40,9 +40,9 @@ public ReservationResponseDTO create(ReservationRequestDTO dto) { throw new BusinessLogicException("El número de comensales introducido supera la capacidad de la mesa"); } - Reservation conflicReservation = findConflictingReservation(dto.tableId(), dto.reservationDate()); - if (conflicReservation != null) { - LocalDateTime start = conflicReservation.getReservationDate(); + Reservation conflictingReservation = findConflictingReservation(dto.tableId(), dto.reservationDate()); + if (conflictingReservation != null) { + LocalDateTime start = conflictingReservation.getReservationDate(); LocalDateTime end = start.plusHours(2); throw new BusinessLogicException("La mesa con id " + dto.tableId() + " está reservada de " + start.toLocalTime() + " a " + end.toLocalTime() @@ -87,6 +87,10 @@ private Reservation findConflictingReservation(Long tableId, LocalDateTime reque return null; } + /* + * TODO (implementación provisional para tests) -> modificar cuando + * implementemos JWT para que devuelva las reservas según su rol (user o admin) + */ public List findAll() { List reservations = reservationRepository.findAll(); List response = toResponseList(reservations); @@ -102,7 +106,42 @@ private List toResponseList(List reservatio } return response; + } + + public ReservationResponseDTO findById(Long id) { + Reservation reservation = reservationRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); + + return toResponseDTO(reservation); + } + + public List findByUserId(Long userId) { + if (!userRepository.existsById(userId)) { + throw new ResourceNotFoundException("Usuario con id " + userId + " no encontrado"); + } + + List reservations = reservationRepository.findByUserId(userId); + + return toResponseList(reservations); + } + + public List findByStatus(ReservationStatus status) { + List reservations = reservationRepository.findByStatus(status); + return toResponseList(reservations); } + public void cancelReservation(Long id) { + Reservation reservation = reservationRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); + + if (reservation.getStatus() == ReservationStatus.CANCELLED) { + throw new BusinessLogicException("La reserva con id " + id + " ya estaba cancelada"); + } + + reservation.setStatus(ReservationStatus.CANCELLED); + + reservationRepository.save(reservation); + + } } diff --git a/target/classes/com/pachedev/restoreserve/controller/ReservationController.class b/target/classes/com/pachedev/restoreserve/controller/ReservationController.class index ccaaf8023e70d3274b04e4a7f9bf6f40d586a2b1..df89e1eb00725a27f967c98afe0d2e4792a7f7fa 100644 GIT binary patch delta 1417 zcma)6OHUI~6#i~WpN!a1Fp3ZeYM=!&Q6iud5qW3}tsqE6@xj0hWpFyh&a{al=|&T` z&BQf3a!g98(t@UlKGb8B!vL-`gep^s0c*qA-|tYVxWA&d$iNk#%@|555AS7V zcy1?XC0xBu{n{H6-`j|bixp(6snPtOf{(&yl6x<@BYir#@gx^3_H89R**K@84i<(F^t=Z zdJZgDt$|B6c}Vo6mN2WUclMQ%eQil&%KR9anC z^|(#$eGapDz;L`WlNgBk(1J%C<{>e7_2h6;Hx=DvxZIKZ89045q)4)oGKSKsMpCTB zA`~}>>0V1>?M)T;9F}mAp4&p+-D-9}cWE5rNLU;0x7LK`f{&rDn9+LXX=HBewP)Vh z3NPBM-<}t&-O74TWu2r04h*7}WF4qCTG!^l3=H82+1!i_!*uY|8YEW-NiO&SeCH>O zY(acax&uMlUE~RLp_BF#1wYcdNuvndK#1;p=o2e2EdBNEq8gNgT_lfl9dYY_5>Z^FB~4=L>$u<6!4U u@S9{UXZ@u0-5c!FZEMo*;TYYolK!OBM+ye%dHXr~5=@tuUU&=AF7IEW#wb(( delta 212 zcmX@5yiA1a)W2Q(7#J9A8GJW#iLy^#$Wz7U%)?;E;4)c}cLf`3Sz=CU>g40RCL#=q z42%rSK&6ZfLJX`7Y(N(CWCgwiR#^s42KmXYd@cSe4B9|Z9-s;a25tr&23@dfI|h58 zJ_jJ14JhLXqy>Q3j)4gX85p>Vl^+z8|`F=zovkU9ni(aD*DTKcYFQ8%D@ j3_yFC7~Fxh2T&GSN&kT#^YF{v%kv=V8nf)(s52$EJ?UVg@66sp_SjA_ z8&{%BS&mQVCX(q~VY-VrxDuqM0>T&;R) zdVLQ1HRKo|)~Ttgkov*3CdO4riFk%Iy67$o+dZvMA1@HQ)KPbn+UluMA6EzM#B7Bf zP;Uk5m$Hu-+0QYev4*vTvtSVfM)mw?c49aym7dV2bCY>u&B{Xd1yMzZ2Sq-pF@{;f z)7LZDmrAEo{XsbCC$3BC`gi8z3%*bXJA!Z-PWOcV%1B0U+7yJl(rzb)mS>ScHK_C9 zrkWr;m002O(yzX%nRPzK%`QH!hHKZ`x9|z|dhL(FNB#KL3v!#rbzEQVY)-$@BFDg$Y zs=0YoxJ!Lk)sYs*DABCq;jnr$;!?4-E~krGL0GO1u3oqn`Q7e00xyojj}i4r!{xvj z+;|C5@sliZ7*1TmMTx#5-YSY4V9{7riTF(n z8$G6w3t}5Wk|Jca2=UtRAwpkp)q&Q}arhRifg23ov1G2X!IbugARz2G;{n!-%b4D$lH5q2`rq^# z%F~nK-MPl-6=@{8twzRy;tllQX_F6&c-x3_z`Yb)(BzIBQ|8sCh)NG!r{uEqv)HU3*tVG~TFsZe_F_Zm0+ zJ`4$yA#qQJ<`RQUODN>hTNIlt5dl+#8UOG-w>>nm%kOZ{>0nce?5xJk>VLrh6xM)IIKQVd|w^{~nU(U#48C|~VL zNE_D)XKmr`;_^Z|5x}G?@2dB$;~cU%C^u@OMwKL z|L75q#f(1bbJ*o5PuU!kPf?d0`5iqr+YZmjlloP|;aT}rPqa_SeB6|$aoOwnhmX5nLA(&Tx05e}Bo#BFr6_i#t{ldaM;sYm8CAxmu< za*$SN%8zuVt$slib<=~P2ngTMzS-G@{~B|98~|R?lbu$)tvCEf+R3EHGU*F_B8P`Z zUukXOQN}<6EFB{HEFWzd0IVn;L!OZuth31z8A_P4#jct?oz-!6LmkfXyvh%wp5z5y GLhUa>Z&g|V From 8dcff7611e499be175b839e3c25b0384ead90745 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Sun, 10 May 2026 17:23:21 +0100 Subject: [PATCH 12/47] feat: add JWT authentication and security configuration --- pom.xml | 21 +++- .../Login user.request.yaml | 12 +++ .../controller/AuthController.java | 29 +++++ .../restoreserve/dto/AuthRequestDTO.java | 8 ++ .../restoreserve/dto/AuthResponseDTO.java | 5 + .../dto/RegisterUserRequestDTO.java | 12 +++ .../dto/ReservationRequestDTO.java | 9 +- .../model/entity/{User.java => AppUser.java} | 5 +- .../model/entity/Reservation.java | 2 +- .../repository/AppUserRepository.java | 14 +++ .../repository/UserRepository.java | 8 -- .../security/CustomUserDetailsService.java | 37 +++++++ .../security/JwtAuthenticationFilter.java | 74 +++++++++++++ .../restoreserve/security/JwtService.java | 100 ++++++++++++++++++ .../restoreserve/security/SecurityConfig.java | 68 ++++++++++++ .../restoreserve/service/AuthService.java | 49 +++++++++ .../service/ReservationService.java | 8 +- src/main/resources/application.properties | 3 + src/main/resources/data.sql | 8 +- target/classes/application.properties | 3 + .../controller/AuthController.class | Bin 0 -> 1736 bytes .../restoreserve/dto/AuthRequestDTO.class | Bin 0 -> 1915 bytes .../restoreserve/dto/AuthResponseDTO.class | Bin 0 -> 1317 bytes .../dto/RegisterUserRequestDTO.class | Bin 0 -> 2907 bytes .../dto/ReservationRequestDTO.class | Bin 2857 -> 3013 bytes .../restoreserve/model/entity/AppUser.class | Bin 0 -> 3230 bytes .../model/entity/Reservation.class | Bin 3476 -> 3485 bytes .../restoreserve/model/entity/User.class | Bin 2952 -> 0 bytes .../repository/AppUserRepository.class | Bin 0 -> 690 bytes .../repository/UserRepository.class | Bin 366 -> 0 bytes .../security/CustomUserDetailsService.class | Bin 0 -> 3240 bytes .../security/JwtAuthenticationFilter.class | Bin 0 -> 3633 bytes .../restoreserve/security/JwtService.class | Bin 0 -> 3167 bytes .../security/SecurityConfig.class | Bin 0 -> 6237 bytes .../restoreserve/service/AuthService.class | Bin 0 -> 3542 bytes .../service/ReservationService.class | Bin 9946 -> 9967 bytes target/classes/data.sql | 8 +- 37 files changed, 455 insertions(+), 28 deletions(-) create mode 100644 postman/collections/RestoReserve Collection/Login user.request.yaml create mode 100644 src/main/java/com/pachedev/restoreserve/controller/AuthController.java create mode 100644 src/main/java/com/pachedev/restoreserve/dto/AuthRequestDTO.java create mode 100644 src/main/java/com/pachedev/restoreserve/dto/AuthResponseDTO.java create mode 100644 src/main/java/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.java rename src/main/java/com/pachedev/restoreserve/model/entity/{User.java => AppUser.java} (89%) create mode 100644 src/main/java/com/pachedev/restoreserve/repository/AppUserRepository.java delete mode 100644 src/main/java/com/pachedev/restoreserve/repository/UserRepository.java create mode 100644 src/main/java/com/pachedev/restoreserve/security/CustomUserDetailsService.java create mode 100644 src/main/java/com/pachedev/restoreserve/security/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/pachedev/restoreserve/security/JwtService.java create mode 100644 src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java create mode 100644 src/main/java/com/pachedev/restoreserve/service/AuthService.java create mode 100644 target/classes/com/pachedev/restoreserve/controller/AuthController.class create mode 100644 target/classes/com/pachedev/restoreserve/dto/AuthRequestDTO.class create mode 100644 target/classes/com/pachedev/restoreserve/dto/AuthResponseDTO.class create mode 100644 target/classes/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.class create mode 100644 target/classes/com/pachedev/restoreserve/model/entity/AppUser.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/entity/User.class create mode 100644 target/classes/com/pachedev/restoreserve/repository/AppUserRepository.class delete mode 100644 target/classes/com/pachedev/restoreserve/repository/UserRepository.class create mode 100644 target/classes/com/pachedev/restoreserve/security/CustomUserDetailsService.class create mode 100644 target/classes/com/pachedev/restoreserve/security/JwtAuthenticationFilter.class create mode 100644 target/classes/com/pachedev/restoreserve/security/JwtService.class create mode 100644 target/classes/com/pachedev/restoreserve/security/SecurityConfig.class create mode 100644 target/classes/com/pachedev/restoreserve/service/AuthService.class diff --git a/pom.xml b/pom.xml index 902314d..42b9fe1 100644 --- a/pom.xml +++ b/pom.xml @@ -30,12 +30,29 @@ 17 - + + io.jsonwebtoken + jjwt-api + 0.13.0 + + + + io.jsonwebtoken + jjwt-impl + 0.13.0 + runtime + + + + io.jsonwebtoken + jjwt-jackson + 0.13.0 + runtime + org.springframework.boot spring-boot-starter-validation diff --git a/postman/collections/RestoReserve Collection/Login user.request.yaml b/postman/collections/RestoReserve Collection/Login user.request.yaml new file mode 100644 index 0000000..35b207f --- /dev/null +++ b/postman/collections/RestoReserve Collection/Login user.request.yaml @@ -0,0 +1,12 @@ +$kind: http-request +name: Login user +url: http://localhost:8080/api/v1/auth/login +method: POST +body: + type: json + content: |- + { + "username": "armymoves", + "password": "1234" + } +order: 4485838375407470 diff --git a/src/main/java/com/pachedev/restoreserve/controller/AuthController.java b/src/main/java/com/pachedev/restoreserve/controller/AuthController.java new file mode 100644 index 0000000..5288add --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/controller/AuthController.java @@ -0,0 +1,29 @@ +package com.pachedev.restoreserve.controller; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.pachedev.restoreserve.dto.AuthRequestDTO; +import com.pachedev.restoreserve.dto.AuthResponseDTO; +import com.pachedev.restoreserve.service.AuthService; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/auth") +public class AuthController { + + private final AuthService authService; + + @PostMapping("/login") + public ResponseEntity login(@Valid @RequestBody AuthRequestDTO dto) { + return ResponseEntity.ok(authService.login(dto)); + } + +} diff --git a/src/main/java/com/pachedev/restoreserve/dto/AuthRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/AuthRequestDTO.java new file mode 100644 index 0000000..76330c6 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/AuthRequestDTO.java @@ -0,0 +1,8 @@ +package com.pachedev.restoreserve.dto; + +import jakarta.validation.constraints.NotBlank; + +public record AuthRequestDTO( + @NotBlank(message = "El nombre de usuario es obligatorio") String username, + @NotBlank(message = "El password es obligatorio") String password) { +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/dto/AuthResponseDTO.java b/src/main/java/com/pachedev/restoreserve/dto/AuthResponseDTO.java new file mode 100644 index 0000000..a856c98 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/AuthResponseDTO.java @@ -0,0 +1,5 @@ +package com.pachedev.restoreserve.dto; + +public record AuthResponseDTO( + String token) { +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.java new file mode 100644 index 0000000..ab93c74 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.java @@ -0,0 +1,12 @@ +package com.pachedev.restoreserve.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + +public record RegisterUserRequestDTO( + @NotBlank(message = "El nombre es obligatorio") String name, + @NotBlank(message = "El nombre de usuario es obligatorio") String username, + @NotBlank(message = "El teléfono es obligatorio") String telephone, + @Email(message = "El email introducido no es válido") @NotBlank(message = "El email es obligatorio") String email, + @NotBlank(message = "La contraseña es obligatoria") String password) { +} diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java index d7aaeb8..4ecb74c 100644 --- a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java @@ -2,12 +2,13 @@ import java.time.LocalDateTime; +import jakarta.validation.constraints.Future; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; public record ReservationRequestDTO( - @NotNull(message = "el id del usuario es obligatorio") Long userId, - @NotNull(message = "el id de la mesa es obligatorio") Long tableId, - @NotNull(message = "la fecha de reserva es obligatoria") LocalDateTime reservationDate, - @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests) { + @NotNull(message = "el id del usuario es obligatorio") Long userId, + @NotNull(message = "el id de la mesa es obligatorio") Long tableId, + @NotNull(message = "la fecha de reserva es obligatoria") @Future(message = "La reserva debe ser en una fecha futura") LocalDateTime reservationDate, + @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests) { } diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/User.java b/src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java similarity index 89% rename from src/main/java/com/pachedev/restoreserve/model/entity/User.java rename to src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java index e3fbc81..d475a04 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/User.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java @@ -19,7 +19,7 @@ @Getter @Setter @NoArgsConstructor -public class User { +public class AppUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -34,6 +34,9 @@ public class User { @Column(name = "email", nullable = false, unique = true) private String email; + @Column(name = "user_name", nullable = false, unique = true, length = 50) + private String username; + @Column(name = "password", nullable = false) private String password; diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java index 284c36a..ecca2e9 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/Reservation.java @@ -33,7 +33,7 @@ public class Reservation { @ManyToOne @JoinColumn(name = "user_id", nullable = false) - private User user; + private AppUser user; @ManyToOne @JoinColumn(name = "table_id", nullable = false) diff --git a/src/main/java/com/pachedev/restoreserve/repository/AppUserRepository.java b/src/main/java/com/pachedev/restoreserve/repository/AppUserRepository.java new file mode 100644 index 0000000..3fdd41f --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/repository/AppUserRepository.java @@ -0,0 +1,14 @@ +package com.pachedev.restoreserve.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import com.pachedev.restoreserve.model.entity.AppUser; +import java.util.Optional; + +public interface AppUserRepository extends JpaRepository { + + Optional findByUsername(String username); + + boolean existsByUsername(String username); + + boolean existsByEmail (String email); +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/repository/UserRepository.java b/src/main/java/com/pachedev/restoreserve/repository/UserRepository.java deleted file mode 100644 index 539835e..0000000 --- a/src/main/java/com/pachedev/restoreserve/repository/UserRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.pachedev.restoreserve.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import com.pachedev.restoreserve.model.entity.User; - -public interface UserRepository extends JpaRepository { - -} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/security/CustomUserDetailsService.java b/src/main/java/com/pachedev/restoreserve/security/CustomUserDetailsService.java new file mode 100644 index 0000000..866b32b --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/security/CustomUserDetailsService.java @@ -0,0 +1,37 @@ +package com.pachedev.restoreserve.security; + +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.repository.AppUserRepository; + +import lombok.RequiredArgsConstructor; + +/** + * Servicio necesario para que Spring Security use nuestros usuarios de la base + * de datos en lugar de los que crea automáticamente por defecto. + */ +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + + private final AppUserRepository appUserRepository; + + /** + * Busca al usuario en la base de datos por su username y lo convierte + * en un objeto UserDetails que Spring Security pueda entender. + */ + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + AppUser user = appUserRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("Credenciales inválidas")); + + return User.withUsername(user.getUsername()).password(user.getPassword()) + .authorities(user.getRole().name()).disabled(!user.getActive()).build(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/security/JwtAuthenticationFilter.java b/src/main/java/com/pachedev/restoreserve/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..055c30b --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/security/JwtAuthenticationFilter.java @@ -0,0 +1,74 @@ +package com.pachedev.restoreserve.security; + +import java.io.IOException; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +/** + * Filtro que intercepta todas las peticiones HTTP para validar el token JWT. + * Si el token es correcto, identica al usuario y le da acceso a la aplicación. + */ +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtService jwtService; + private final CustomUserDetailsService customUserDetailsService; + + /** + * Proceso de filtrado principal: + * 1. extrae el token del encabezado Authorization. + * 2. valida la firma y la fecha del token. + * 3. registra al usuario en la lista (contexto) de seguridad. + */ + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + // extraer el encabezado Authorization + String authHeader = request.getHeader("Authorization"); + + // si la solicitud no trae token JWT o no empieza por Bearer pasamos la petición + // al siguiente filtro + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + // extraer el token (7 caracteres-> Bearer + el espacio que precede al token) + String token = authHeader.substring(7); + String username = jwtService.extractUsername(token); + + // si hay un token y no está ya autenticado, buscamos los datos del usuario en + // la base de datos + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = customUserDetailsService.loadUserByUsername(username); + + // Si el token es válido (firmado y no expirado) creamos la credencial de acceso + // para Spring Security + if (jwtService.isTokenValid(token, userDetails.getUsername())) { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + // autorizamos al usuario en el sistema + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + // continuamos con la cadena de filtros de Spring Security + filterChain.doFilter(request, response); + } + +} diff --git a/src/main/java/com/pachedev/restoreserve/security/JwtService.java b/src/main/java/com/pachedev/restoreserve/security/JwtService.java new file mode 100644 index 0000000..abbbe86 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/security/JwtService.java @@ -0,0 +1,100 @@ +package com.pachedev.restoreserve.security; + +import java.util.Date; + +import javax.crypto.SecretKey; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; + +/** + * Servicio que se encarga de : + * - generar el token cuando un usuario se loguea + * - extra información del token (como el username) + * - validar que el token sea legítimo y si está vigente + */ + +@Service +public class JwtService { + + /** + * Inyecta el valor de la clave secreta definida en application.properties + */ + @Value("${jwt.secret-key}") + private String secretKey; + + /** + * Inyecta el tiempo de vida del token que hemos definido en milisegundos en + * application.properties + */ + @Value("${jwt.expiration}") + private long jwtExpiration; + + /** + * Crea un nuevo token con el nombre del usuario, la fecha de emisión y la fecha + * de caducidad, firmándolo con la firma digital + */ + public String generateToken(String username) { + + Date now = new Date(); + Date expirationDate = new Date(now.getTime() + jwtExpiration); + + return Jwts.builder() + .subject(username) + .issuedAt(now) + .expiration(expirationDate) + .signWith(getSigningKey()) + .compact(); + } + + /** + * Convierte la clave del application.properties en una firma digital válida + * para el algoritmo. + */ + private SecretKey getSigningKey() { + // Decodifica la cadena de texto que hemos definido en application.properties + byte[] Keybytes = Decoders.BASE64.decode(secretKey); + + // Genera la firma usando el algoritmo HMAC-SHA + return Keys.hmacShaKeyFor(Keybytes); + } + + /** + * Abre el token utilizando la firma digital y extrae su contenido (claims). + */ + private Claims extractClaims(String token) { + return Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token).getPayload(); + } + + /** + * Extrae el nombre de usuario que hay en el token. + */ + public String extractUsername(String token) { + return extractClaims(token).getSubject(); + } + + /** + * Verifica que la fecha de expiración del token no sea anterior a la actual. + */ + public boolean isTokenExpired(String token) { + Date expirationDate = extractClaims(token).getExpiration(); + + return expirationDate.before(new Date()); + } + + /** + * Verifica que el token pertenece al usuario que lo envía y que no ha expirado + * su fecha de validez. + */ + public boolean isTokenValid(String token, String username) { + String tokenUsername = extractUsername(token); + + return tokenUsername.equals(username) && !isTokenExpired(token); + + } +} diff --git a/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java b/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java new file mode 100644 index 0000000..d947794 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java @@ -0,0 +1,68 @@ +package com.pachedev.restoreserve.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import lombok.RequiredArgsConstructor; + +/** + * Configuración de seguridad de la API: + * - Define cómo se protegen los datos (hash). + * - Configura la sesión como stateless mediante JWT. + * - Declara qué usuarios pueden acceder los endpoints. * + */ +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + + /** + * sirve para hashear contraseñas (hacerlas ilegibles) y verificar que la + * contraseña que introduce el usuario coincide con el hash guardado, sin + * necesidad de saber cual era la contraseña original + */ + @Bean + PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + /** + * Cadena de filtros de Seguridad de la API, los controles automáticos por donde + * pasará cada petición HTTP: + * + * 1. Desactiva CSRF (no la necesitamos, nuestra API es stateless gracias al + * uso de tokens JWT). + * 2. definimos que la política de sesión es stateless (no guardará la + * sesión en el servidor, cada petición debe traer credenciales). + * 3. declaramos los endpoints a lo que todo el mundo pueda acceder (permitAll). + * 4. declaramos los endpoints a los que sólo puede acceder usuario con rol + * ADMIN. + * 5. declaramos los endpoints a los que pueden acceder los que tengan rol USER + * o ADMIN. + * 6. anyRequest().authenticated() .> cualquier otra ruta requiere usuario + * autenticado. + * 7. addFilterBefore -> antes de pasar el filtro de Spring Security debe pasar + * el que hemos definido nosotros, que leerá el token y autenticará al usuario + */ + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http.csrf(csrf -> csrf.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests( + auth -> auth.requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll() + .requestMatchers("/api/v1/tables/**").hasRole("ADMIN") + .requestMatchers("/api/v1/reservations/**").hasAnyRole("USER", "ADMIN") + .anyRequest().authenticated()) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .build(); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/service/AuthService.java b/src/main/java/com/pachedev/restoreserve/service/AuthService.java new file mode 100644 index 0000000..ad606d8 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/service/AuthService.java @@ -0,0 +1,49 @@ +package com.pachedev.restoreserve.service; + +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import com.pachedev.restoreserve.dto.AuthRequestDTO; +import com.pachedev.restoreserve.dto.AuthResponseDTO; +import com.pachedev.restoreserve.exception.BusinessLogicException; +import com.pachedev.restoreserve.exception.ResourceNotFoundException; +import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.repository.AppUserRepository; +import com.pachedev.restoreserve.security.JwtService; + +import lombok.RequiredArgsConstructor; + +/** + * Servicio encargado de autenticar usuarios mediante JWT. + * Comprueba que las credenciales introducidad sean correctas y si el login es + * válido genera un token para acceder a los endpoints protegidos de la API. + */ +@Service +@RequiredArgsConstructor +public class AuthService { + + private final AppUserRepository appUserRepository; + private final PasswordEncoder passwordEncoder; + private final JwtService jwtService; + + /** + * Comprueba las credenciales de un usuario y genera un token JWT. + * + * @param dto el username y la contraseña introducidos por el usuario. + * @return token JWT generado tras una autenticación correcta. + */ + public AuthResponseDTO login(AuthRequestDTO dto) { + + AppUser user = appUserRepository.findByUsername(dto.username()) + .orElseThrow(() -> new ResourceNotFoundException("Usuario " + dto.username() + " no encontrado")); + + if (!passwordEncoder.matches(dto.password(), (user.getPassword()))) { + throw new BusinessLogicException("Credenciales no válidas"); + } + + String token = jwtService.generateToken(user.getUsername()); + + return new AuthResponseDTO(token); + } + +} diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 7528b5a..cd6964b 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -12,11 +12,11 @@ import com.pachedev.restoreserve.exception.ResourceNotFoundException; import com.pachedev.restoreserve.model.entity.Reservation; import com.pachedev.restoreserve.model.entity.RestaurantTable; -import com.pachedev.restoreserve.model.entity.User; +import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.enums.ReservationStatus; import com.pachedev.restoreserve.repository.ReservationRepository; import com.pachedev.restoreserve.repository.RestaurantTableRepository; -import com.pachedev.restoreserve.repository.UserRepository; +import com.pachedev.restoreserve.repository.AppUserRepository; import lombok.RequiredArgsConstructor; @@ -26,11 +26,11 @@ public class ReservationService { private final ReservationRepository reservationRepository; private final RestaurantTableRepository tableRepository; - private final UserRepository userRepository; + private final AppUserRepository userRepository; public ReservationResponseDTO create(ReservationRequestDTO dto) { - User user = userRepository.findById(dto.userId()) + AppUser user = userRepository.findById(dto.userId()) .orElseThrow(() -> new ResourceNotFoundException("Usuario con id " + dto.userId() + " no encontrado")); RestaurantTable table = tableRepository.findById(dto.tableId()) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ad91b45..1dca894 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,3 +8,6 @@ spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.defer-datasource-initialization=true + +jwt.secret-key=ClaveSuperlargaYSuperSecretaNoSelaDigasANadieHazmeElFavorMiraQueGuapaEs0987654321!! +jwt.expiration=86400000 diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 5076487..2b43632 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -10,12 +10,12 @@ VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); -INSERT INTO users (name, telephone, email, password, role, active) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','1234','ROLE_USER',true +INSERT INTO users (name, telephone, email, user_name, password, role, active) +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '1234','ROLE_USER',true ); -INSERT INTO users (name, telephone, email, password, role, active) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','Admin1234','ROLE_ADMIN',true +INSERT INTO users (name, telephone, email, user_name, password, role, active) +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', 'Admin1234','ROLE_ADMIN',true ); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) diff --git a/target/classes/application.properties b/target/classes/application.properties index ad91b45..1dca894 100644 --- a/target/classes/application.properties +++ b/target/classes/application.properties @@ -8,3 +8,6 @@ spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.defer-datasource-initialization=true + +jwt.secret-key=ClaveSuperlargaYSuperSecretaNoSelaDigasANadieHazmeElFavorMiraQueGuapaEs0987654321!! +jwt.expiration=86400000 diff --git a/target/classes/com/pachedev/restoreserve/controller/AuthController.class b/target/classes/com/pachedev/restoreserve/controller/AuthController.class new file mode 100644 index 0000000000000000000000000000000000000000..17cc9fed9fdd65492da3b5b0ec66968b09758583 GIT binary patch literal 1736 zcmcIkTTc@~6h2dED@zqDiUQ&VFO(vTfF>qfB3@%sgaBz`d@$4Q*bd$9tlMb`f5Bg) ziMROR5Aa7Bzge1=CX}=fKFrQJvvcPA&h^jVU%!cHogOAB#%QIet2_|JvUFvGhq7ig zJ~C`bUevw`wdcu@Z`MuuSxZgQAfwR((Gc7deu-}v4rI|V8Wy;=2U}H?jCij5(-r`i zqIa7HtBm5FE-9bU+sqlJuF;X%uKZXB+voe+t8+OWmUt})m0$W0imE)+VTG5C3HYw8 z1=_F47rs&E2pB_qs^kk(55e%$|H4`8iT^P(>%azxP3+deu`2VbrV5_i^nGoFQ6SIg z(Wwp|$^tJa-{qopm+$DB*%Cp3NZ5cjgja{73EqtCOee^Wu+cIy>_D5$9f*nujo>g> zZbX9Tt%xUkr_xN&Q|;Qg4?feb93n=SQWU3zL*sNcNmm#x^i?G(O40|!)>eYe__XTQ!Q97rK!pnBKzmmQTg^}*6Lj$ysq6{s<$&7u%+0*r~C|@cY z%dyU3wrEqu=yA_2hLK|0vk-yo$21CLyFpW21d2D7xSgDI3+*w0*^GSK9)T2toTPJ* zha%0PVeA)hn!-B&j#v%a2+oF4do&8+B32sf3G^8DRQ4;4&3~uMKWXwa^cda5Hvw~^ zNk9@hF}g=HbQ>Bh=nmb*|9gzcA;*sft$m2BhCFsilJNA0z9}Y$V7ytkO literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/dto/AuthRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/AuthRequestDTO.class new file mode 100644 index 0000000000000000000000000000000000000000..ffff8f9d45cf23031a97efe0f9d9f57989647933 GIT binary patch literal 1915 zcmb7E+inwA6kW#&8OI&tB$&{og}6Y7?F6Q2DZLnINodtbDO4fK13VquNAZyH9Ct3R z`cFK7RD_g=en3B}>N@t=9>*q99_DiP?6vl7?fLh=(|-Uw!cP`50$UB;b~>`rQccx$ z0u@G@Hx+c1(~Pw9B#v4K>W`TAr-#oi%nB^LmR;$1(m!?%R6_?%fox2KFWX9BX@B(j zMHIOHaZO<5Aoe4-t&ZH#t$XT;@9RiLuJ%KL3IQiFh@{h%p4;r1od!Qefpq;Sbe`#G zhtW>RW3^Qn%43GP^|M#*H|`|LTm#&UR!N z{@E*Gm4t)S1W&PSvK&-BENgaGH`P1@7HwR@oIqjU_0_YuU01=OWTB=Vx*@$IW@pYl zqXAsCVH-d;(tWlij^tAt%UBWEDeqroD7C4%Wm__AnfwH1%9TCCf8E9^ZU}5&lz&fP zjvA02$91)w>csQU{WLxXeCYaa^jKgc9WZHq3X)hVM*=s; zr~SJOsK`hKDY@k^gR+g=sPx)G(86yjYU$?CnI-04=s0MoUtBZ5E8`n*&&YtN9j&8~ z3hngaVGa(qEqp1kHWEtESH073Q{rjkD|{_5Umq|Cyi1qp`dxjZoWA$3(rKqK;}Dr^L9hg*<_?0Qst+K}|_)g(ze`k20wi5KQ@FN+6M{$TTKgXJl( zJG_bl_MY#npchqz7P!Cbqu!iTriDiWH_msZK>9u5OaCu~ZSK_!b(q1D*+xDso<5;9 z%WFPFjt3wQaRnpOl3Bz_-~6O+o_>PNml@?_%*7OQfx*^^5PV*${*8sVd@;$PM`Z3y zgci`no<=)NVnmzAS=!=RT5CwV$p6V@k{ied1DQ;$>M5=PXF(KtlFK505}!>0eGa_o zxr_=(gVs<=KxIZUMP;j-r}*MLl;9$+V-|Plea)md9Mtf&U#~+#GulG+12(Fg@3Bzb zdXM6}kLF_ry^72-9|O*1R-e4*QRRpE42KDJ9!z1A_YAghmrv8O`?LvsgKzO2zNan6 c(+_xv$5_FSJm06c5t*3ONv@};DIfCx129#qB>(^b literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/dto/AuthResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/AuthResponseDTO.class new file mode 100644 index 0000000000000000000000000000000000000000..cbd09894acb4a96110d6bbed8cf34a979d6f8965 GIT binary patch literal 1317 zcmb7DT~8B16g>lNx2=l>1QfsU1EgJq74)rYq7WlV6$69^eVT4>=t8?wXQxg4Pd-SZ zG2y`<;Eyui{gAGRB)rVrxpU7wbMC$K^VjhY0MGEm!id1Kr@C%WdTnaaz||BQ#S7^H zxh)u(1*WRC zv)s9L)uIvv$~N*S2viz=KyUlqCh1+->`;_Wd9t%7wI7{RXVIu6^(4!v*cijOz1V6j0mP|Oki4I?ZO0`0tNcmmz_{xx|*rLR`Y;7Q?I>`^3U3s z!F7R+i{?KoAu~Sp1K&J9#hA9DwpYd|Zriwlxme*IH}egdwrahTT6W2h=Es(IR9}1a z%8yzwb!q}uq6RbUrBWs|TJ{orSjGYtEj$pI8;*eP2gdJ`n>o zBlQyEb-tqXg^dwh;PZ{L(VA^-pY literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.class new file mode 100644 index 0000000000000000000000000000000000000000..c41a51f61ed1215eb14ed8417d4d0d08f6699c05 GIT binary patch literal 2907 zcmbtV+fv(B6kP{=!x1rH>_EB1B{wi3Zrb#cI&H%(4XFbup**zH2VtLrf-Sj{OsBul zzvyFU`cP)dbcTn%@YavYbREf(g>C2zeUN>$&)#dFz0TfC|GYi@8^CAyI*9>+M^&p) zYUNnwG1(hGjYe3k*2aZC5WH>2;&# znM+k}xwdYYu2b5y+!bazBu=s+9Y^11nu+y#!L%BCwk$}eVC~h7eVwN_EPl^_S!g*eod`XEL$0jL=7D80?-V2WdiL87mf7nWmkr&h3*7y$n%0#c z3z{RG7gJ!VwpD9Yjhe;6)ESP>ekC6)=tk(&U23YSJI+rY{WBH4K-Nf_BhQZY^F`;L^gIiLF{SIRsvjhBy)enTlb`%~oSi+B-V$O5Lbc)$6;YM!kDR6)>YgLqe@? zYPjyN-qkSTuWo4=S8yrU@^g)`@9t{2je7zw<||j}ymH?PQU|(opsElUoL|^bc@{Oy zAK`6l?~Saw<%Mrmn)fs;;i14+S4`6nmx-Skrr~}r@O?CK z`@$k2|I4zl%icRbC$Dsy1I%%cOlKN{_(;S1_}C+tK_f4vdtlW2P&CZlTOt(n$(eK$ZDNK7B#e)|1JXCC;Pc>ZTC$k zfhQ@fQ6QB5lmj6uhXPaf#Go9Eb%sL6XPVC}p8}sb0d2!HrR^O{I!ROn?sUlObCQ6E zBsPh2xhwn6CyfhV+3}DgKFD^DV@yc|29V_2HCj`Ah8(2%0t`cB(J_rFb?EgS?)4oe zF7>0=H`D8zp)VDt?K{r@QU45MLFTL%G=TvgCfMtoE6aH5Qg5zYZ>}7axW$oY^%qq< z9;x^SNw~uZp|~pkiHSeB7{ELHO?bv6raTMyruBmj$?3ojqZ@X*8#Wbzz1|Nt4BZ?h8>E)j)bt6QcnDU@=mI3PpU$9s&pjPc$g~EVd3p`U~@gNbKS7lLfBD$ck*Oi zae0q-qOK@eJmqzvJBSk2zNArpL>Br46@lM9m(I;bgO>219W>8O1N2N47f(^TEJ{#f zKR^tR=)D|gcsN<%lH(OvGeiw|4s6-Zw$IE{Iwc5KDE@^Hi;E|iDBeH8baCkfv&G5S z2{$I=Czw0FuoN}aFObOdFE~z~#I@JMd6w2*XY$mC;6!TOwrBIPaGk>^1j@xZP+<3i zBM*);IF4ZDkNG=*Pf_+P0xKLjfmN*I8NS4GZ15F_!}4Nhfu~K5kwc~%$#%Pn{Q^$%-h)4?(oLe`ey*sxVaX7 zDugXJg*CcRepD{HIRSe)Zx!8=TYQ|i?z*>Ki>l=ota2f5-E;5U=kA%x7h*#kkSofl zI4GYhw#dkLO3D~8(TOhkUFkQ5OdQ6r+*0zPqb5c$CNL?5Iw4=H{h@IaSxg8l@n&3V z+AHIviAhY$Rqd8BhqD39ztq#}0?tWW|Dw$XY57yn_9gcUq`1;twt%@t1!xto=OtXQj6aCv<|1MOWFiN$#P_3+X zQ-T$iWslF=!G>~JA`fFTGC>(|)-eb)p?EDCKqEM^185X@Ps7GH~+;c0z0%Cy-Bs5i;c~EPIFfvD}%%A7k|!0WE-R delta 1099 zcmah}OKTHR6#ni!)0we0567lyjg6foourz!K5DC08(*pQ-9=YI7u`t(18!WzwYZV{ z2gHRSxUgUlg6Kj-aOcXM|3MI!>NnHSOh$@}Gw0lM&v(9iKhB*``(L`&m*1WD08V3d z%X%c_ckx+_$`?va9F*Ubk~k#)DC0)aK@urhRExInpoEG*qrCETXjVQ}C+#B+s;CLf zZKr;yW#g!WX&jSz?W-~G;5ZiKFKx{@hq{R+xvEd7=W#(k)IVxx6SypwjM+kNuRxww zpm_pLV{>!ke*Nxxv$@eUah*x|ME6t^H{}Q8dCP`Cf;P~^5Nuvx4@4ThR4Ohj8;W_D(Ty*=&bMA@wWDM+ZK-{sL({W@BbC* zv7{OHD->t-;;hcjWs)48GDHgjrbMv1Fu4Xdy+>%)0pXk z%4AYlneZze%#wL`nBcUYfQC7iFUV1AxD^?+UFRq`**qz9`Vs#vPWqKLa(=aqyg$`O z!Ov=Kcw5|!&PVUC!35W#yL8=2wOmRQ#PXe$yr=Hu(=OwF{ZA2<)Ix5D;b5?&X?)ZH z-x(~@r`YOZIG2~uz%s621y`B3n7@V_xW)Or&GiG~bh&Q3@{aALVy`Or2|Tki$$wtS EUsh&mZ2$lO diff --git a/target/classes/com/pachedev/restoreserve/model/entity/AppUser.class b/target/classes/com/pachedev/restoreserve/model/entity/AppUser.class new file mode 100644 index 0000000000000000000000000000000000000000..512da080eef4520f18e864e60cbef5c3e828a155 GIT binary patch literal 3230 zcmb7FYgZdp6g@X25a>|eSPIs*Rt;2{zO+h9E2Sw$(}0jhi;p@a*JRpx#CgQUztXkr z(&Yz#fIrIBeP(W83UST!1DSiz-Dlr(&)Mhj?|*;)LqzM;9HE?`)uwCfU8C8NE!opO z=?5-;q}P+W?Y5+)OD8acLw%#$eFF0cF!ZkUeA5r4)0BFp1-B~$kG7;EJtL5< zhGB&gu%aLEzI~YDf&1!*-N>Wzuw1KG>Q4pb9mAG_rV~N$1)dp&y)^LtP1g!-hwpVl z%QEnuA!L#S4O`M_2OZws5HuP{OLjZ16CKYADoEQfExrVe9$qWt}IzgDjPSB;03iv6tl0r zLCJz+xuMZGofV`3TJM*c4?fmtmJf0|+y8|d!G zGU&Li9mB&b0(30mghQOLxulEA7dvS{9Oa1{R%Hh{!Lpx|c-PNF%p1S|X6a3z&$o_( zyZ`L@WMutUGidm(>6pPiK|`hG#uyFJHyYLH$rwGQ??z}}(A4oXQ+^tKPd^~MsCBk~ z7BpYlPl~-yFKBGf4ZWs(WO5NTH9S|DP|)DvQjX>AtP~fm+`3Z=@QDu*e^U+0lzUX`cqHcr46>c)ZAw(a#j{?fEZq?}`I4@xRj--ot@C3F1F7<(s z>}i_I@#z#!uF!k%cpvHD^n8GC34c1)0^h>QAGG`%7CE|#UxpBJ@gaV-7(gG?P0?eT zA^IKqI<2%DS?S}ksOT{RLBC6TY4qi+^zkrN^cbR`$Nxg;_oVLc!^Wd>eC1DStmOZq sZ(ma4ma~Y6XpZuj2O7S|^K@LbL5_Zmn&nyaAB%_jPuLqE#c)4(1?9OMf&c&j literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class b/target/classes/com/pachedev/restoreserve/model/entity/Reservation.class index 5be81a389ca3bba38dcecdb6339d2b1c99181214..9254c014803677a58c0249e2a3d5bc799a586ad0 100644 GIT binary patch delta 54 zcmbOtJy&{z2qUAxLzJo?w#hYF*q693@$_Hm zIdb&)!5`p{^7zc|O&e%DyL`y*-nq|x?#w(hlYjpG>u(~uM?dB%BWSJd+Irt;_hd&7 zbWi$$i-+_EQn%fXv~=kNW^klGgDp>4LGv$+fuUQ5)75tlU&wYKC}VbDub*txU8j3j z(1pDahi%z1ee=+gn~viKMqs**FX%krFAXm+^uF|b(+{N6mU^v&V=H5~wxuIIBaoez zVTBT~DL>$K_bA~6?rR?PqZ749)kd?{d@d;K7`7C&Fyi!n;F*!zm&V?|nKpIKVq$Qnh(Bsv*py@zbvfp!^XuBXNCvC&D01F-S=PC^+wjw92j&1r7DsaP z(8Y}gN1yA;pa!oO%Bv&$O7JBmc+DA%k$jb0&h%VqspSP@iUD|Y&-+wuF{n4UublJ?R+82C1@QNzNN%WbWfv;w1mO} z(Wa87_?quSaTvX+(K20uOnxlWV);##R$J)Rr!wfdon6C2DgyK${KSDcF~!j%DldFU z1L8Oz+Rh z+L@t!dX}fApmQhFNS)KDMc=}`s3UfM5>zT5jMngwUeL_G8+vW|*kswJdGcLnHbLVj zLp7R^D6XZkO%@d_c5@+!e7XXacvfKga=~Mj$LbtbQ+Tt6Gm_4b1_R1aZ5Q#L!?9T( z#`y!l7izQ!2nzBvq;L!=0)p~94JpNtcMj-0AgELYnU5~Q)jo{TMHwzXwYvoDC49dt z=`y_sk0Rj3ArO^!nr1oPox{%ibQLF75GIHH0p1_tr{i1TovZv#*M7xEhF0-p2qDG_ zo>~l`HTnoEbUj^9AkxzjeUeDDo{s3#M50^ih(1pwx}A>b4&6=2(MCF=`}Ac3(SvkE z4`~Y}{HFYOK%QZ}D{HUl+Hd$8)xQwwUy2b*f3A(Va~=VmLuV0}ZIvD;(d%jH-|xx`h^BWSkr2klm}f70G78oA{x mJR&Mm7Sl+>`(#edsyfZkcR*z5c{Jg9SRP>gJxDQKfA|l%Wctbg diff --git a/target/classes/com/pachedev/restoreserve/repository/AppUserRepository.class b/target/classes/com/pachedev/restoreserve/repository/AppUserRepository.class new file mode 100644 index 0000000000000000000000000000000000000000..83b523984e38f91c52d9cd3d562db74887cb69bd GIT binary patch literal 690 zcmbVK%}&BV5S|6GfP(n@40v%*da00Tq6brhk$B;@wnJHLyPNG6Kmw8mm0cqEdL_io4NTL)Gd94Zs?R2!LuLnC6mSLTif!U{gtqFD4oi)BFu&7n+! z(`nu(p)>~Vwzq81w|PN#e-33Th>%?*J}wmSLckR~XI4vB6q3331>3 z4L4*7{E6Mngp^Xzid@x zX-e5@#tKvz`FyEhMp>aM#eoyeS-F_AbN*FGO11Y;S(ux>HR0?46RTM2_!;1~v8Xb|WX5WjRS` ziX+FhMiT3!o9$p2J#Atqb_q03sz{(a(_7;+**FnQ9(A1XV7I}552Jz z&qO!&3T*O&vu>!yrUHLfpueun3V?NS4bhi1uwUTt%9NDJqH8HH(ve*clr;9C$HW0V zNBOj5Awaf0v?vi#kB~Gn=rhnOaCGh7HE|G!1k70{np%)Ewk+vuNAF1@Ja$|=p?e<= zo3I|5bybETFQT1BKVCF&RABJGNigw}wxzXF2R3kgQ9`KkrP)1U;uX9qV36HW-{sXi zN@gGLrMMg>$w8lC_?*O`fq^ITZ{tZ5uW1fa`ete4hfJJCj=q+0H03kLIVu#mmbv-= zWP>G6IBVh@&I@d$^+UW9GqivEqR2Y!(?IFb#EZCW;1bi{LSm{fn#g0AkyEVg9hYuQ zoaRjL4Q>&aj85Me6;K(Xs zmdhDL-du=eX?j>z>I%0HD~6MORqBctsesD2oOC=V8f2Z#tb)I4>FOO9*gYCEVU^WI zZ^j87QZ(dwek7AJquF!)a?zi*&MQv^GEz3e9OOpsAz;41F^vVg=;S6R{U`PfAFATnU+_FXFH~_P^*fGTZ|30TDzb$tPVx6l z6&JpR(fmD9sVc5iG5SLyZk+F3XyLnyV!PQ=b)l20^_SQt>O!2Y*J}j~q2Y>#O$xTNB+nYrE!kOD9Cmi!jjMp4Oh`5e?gym_pd! zU|#QV?qXWg&vR>Pw**cI*9QZzQ9-PCi~f4 IeT|*}0_GyfKmY&$ literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/security/JwtAuthenticationFilter.class b/target/classes/com/pachedev/restoreserve/security/JwtAuthenticationFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..eee3f7b8ee204efcc8f7943221446f391e339511 GIT binary patch literal 3633 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 UpU}>y_?*70^85l{;TyF73leoR8~^|S literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index 7363adb31a9353e437e825fdfbd14fcc3b50f2d3..b80a3c6c31497b3222f02754855d9c674cb6373f 100644 GIT binary patch delta 113 zcmccR``&khBqO86 Date: Sun, 10 May 2026 17:48:54 +0100 Subject: [PATCH 13/47] fix: replace plain passwords with hashed passwords in data.sql --- src/main/resources/data.sql | 4 ++-- target/classes/data.sql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 2b43632..e037407 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -11,11 +11,11 @@ INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '1234','ROLE_USER',true +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true ); INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', 'Admin1234','ROLE_ADMIN',true +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true ); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) diff --git a/target/classes/data.sql b/target/classes/data.sql index 2b43632..e037407 100644 --- a/target/classes/data.sql +++ b/target/classes/data.sql @@ -11,11 +11,11 @@ INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '1234','ROLE_USER',true +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true ); INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', 'Admin1234','ROLE_ADMIN',true +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true ); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) From 8b54d0b973730a2dab114c5cb2c1e4ee521a1f95 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Sun, 10 May 2026 21:32:38 +0100 Subject: [PATCH 14/47] feat: show reservations based on authenticated user role --- .../service/ReservationService.java | 35 +++++++++++++----- .../service/ReservationService.class | Bin 9967 -> 11010 bytes 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index cd6964b..92aea82 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -4,6 +4,9 @@ import java.util.ArrayList; import java.util.List; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import com.pachedev.restoreserve.dto.ReservationRequestDTO; @@ -26,11 +29,11 @@ public class ReservationService { private final ReservationRepository reservationRepository; private final RestaurantTableRepository tableRepository; - private final AppUserRepository userRepository; + private final AppUserRepository appUserRepository; public ReservationResponseDTO create(ReservationRequestDTO dto) { - AppUser user = userRepository.findById(dto.userId()) + AppUser user = appUserRepository.findById(dto.userId()) .orElseThrow(() -> new ResourceNotFoundException("Usuario con id " + dto.userId() + " no encontrado")); RestaurantTable table = tableRepository.findById(dto.tableId()) @@ -87,15 +90,29 @@ private Reservation findConflictingReservation(Long tableId, LocalDateTime reque return null; } - /* - * TODO (implementación provisional para tests) -> modificar cuando - * implementemos JWT para que devuelva las reservas según su rol (user o admin) + /** + * Devuelve las reservas según el rol del usuario autenticado. + * + * ROLE_ADMIN -> todas las reservas. + * ROLE_USER -> solo sus reservas. */ public List findAll() { - List reservations = reservationRepository.findAll(); - List response = toResponseList(reservations); - return response; + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + String username = authentication.getName(); + + AppUser user = appUserRepository.findByUsername(username) + .orElseThrow(() -> new ResourceNotFoundException("Usuario no encontrado")); + + List reservations = new ArrayList<>(); + + if (user.getRole().name().equals("ROLE_ADMIN")) { + reservations = reservationRepository.findAll(); + } else { + reservations = reservationRepository.findByUserId(user.getId()); + } + return toResponseList(reservations); } private List toResponseList(List reservations) { @@ -116,7 +133,7 @@ public ReservationResponseDTO findById(Long id) { } public List findByUserId(Long userId) { - if (!userRepository.existsById(userId)) { + if (!appUserRepository.existsById(userId)) { throw new ResourceNotFoundException("Usuario con id " + userId + " no encontrado"); } diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index b80a3c6c31497b3222f02754855d9c674cb6373f..dbb5d60a56a3f4aabe1bc5a6065f1066e8c4c630 100644 GIT binary patch delta 2833 zcmb7GYfx0@8GfGKWzVu4kX`P#6%=r}$O>W<2r3E`m75rBrHWl)B}1)mWFsbx zX>6+bNTRi^>2zvmI?Xh#DF!o*`PI%e(_fu-`YVkyowm-jO?tniN&23{ikPuo=9js5Y_P) z{FQJ{1mY>2X!q9it{67CQrxx-{2l+)@DHM8#V9KU(eW?5Pq>6uMw1Z!%CYD57UU%UNw;Gv1-)RS91yqX-CM3YC3ghP$x90a3~TAsJ={Z zVPY!gGa?&6o0D10*0_pjP|UAxz5Rnu7p@XEBfWz;(z~I7-mb$PoqfCdofu`F&U^}E zd*E2YABquq>bxblcq9YQut>-6@O#ORZyDQCon`c>Xl}F939?*g1=px4_m*Uq|6{5* z{r{2^j>M8o?%qlNalbF*4^Q~^#f@=o6H%i9F*t5ER*0VSE*d4`s>r=oy_4=x=Nu08 z6KA>_%-Q3~z+0@6=trZVN}$*4#2R zn$uopbn!l&n+T-(Z92+x3{a)4UKJAf0=)h6%I;qj*x%q$hg0< z(T()w`zE484Q7_9_iaweSzPVlC#6Zlv4lSwjFg8X<$jMtEFzeb zaUR#0P`@s&OFKbakbx~JR1GDwwwQwtt5-{QS3auq0S-%$&)x|#tMgMlAlNCv9Q>Th zD)nYRt@G0+c*X?3pt?)zwJ#DSeSw(zN@=0h!Siaaw8=I{RH?y|0?CtQ*Z2bANo_Ti zQ&!JaSGSj0c%TxQs6r7cQH*u+=fy^OHlr3@sKWpna0ose!3G4;h!~pim{=%zzlhCv z30rUlE%-KC@qM)64cw0(Vk>@*?dp=}r6HmIKkL9$EeJ`aMWPFC5r|EcBxQ zgOXt{Hem?c@Bn%+EUW$U9K>OasNwQglFWidoPe>{@ZJLcM)cM#;NL`F?W@SDU%*Ee zT&ES7NBNtm?th*31J~$iwXL6L`aCmp=$>cpMYzQL%mNFER@*n>Hg;lByucERAhkud zd9Hp%(%3L3&jw`3Gl29YCXg+~7h_U{3Q8d%5hN^hBiM~;^x+r|iZIWMdGjJqyddHL z=@{W!R)XTck5#OeI;zo*FX0lb5~G>x@GD98Td~!}Y7x05$TtKjg5@I#p0-&7HGH69 zR716fw;;kAqC`>ZJ;gg*EEU3XkiA2tFficv#vxBP<_P z?^fjNW_mdQXkfi^ttrkD;Fj<(0j^gqYpT5hL7-wb2zevTguF#-lo|TfEzoB{-F0qI zPp$DJPr-><SSz=w0SZNek(inH;QdP}nx;fb! z>3l}7CEX^A$d~&%w;ZT@4Tr9?6}ZOs8zvdA*~-4!`nwwG7CnJ8676YG;4EtJtm<7` zo@-WbSrWencCJ&8uPsT+J+wq(yGbIDs@a?%A<=q!<)SLyJy!31&$Ca!P})yCm;U4gB7|`gz>=9zStKbiwwN zF1tk+bl#{JTuoMbHeHa`T$I*a5a}Y z>l;(NFsyr=uc+wQROkWo{TqdY7NL)Fhn z+x@a0mH&kv6t#m4amrkUd5lq>6q^}y{|Fyr(p){x(|p4GCIEYIr?s UuFs{O&q)sB!=?B1&WgVY(|2a}Woq{h zW&3Me1_v_-JF>YUJMN(0fzNSI#lOU?rc7@yp-5!xKH4aP{#6bWnN?_1K&Tb(_{&7K z&&zWq+biZ5HHee`OsPX9RX=O*JHV8=B?sGkt>hv!iyOgayBB71tH_c^>?%n(ok(*X z<16-+9#*M}s;60~SqubIVK*%{Hc~3p$crfxo9qqPKWZEkO7FuPlv4i}f~Hh4FvG)so9mtmX4)8QmN zCf*K5Xsfs!uBXSv-EiF8$H$LHG|?VOlIzUAh(bN(aH0aGC`SO5s74fXcx>QV8ZmSs zfz3!_8)hPlS;(Ol&tf(NkFRjln`p!s=Hevg;R7_`BIe^$EW}lA+?Db#oh_J#CD71{ zDzu1qD^AU9Ljl@hLmOOJ4nI1Xbp^9@VG&j`x{80Rv6k^ISTBywI6aIZC@}Gd?Tx>W zk#qQ!&=F6Z#c!R-(~O#Mg8yAIVhilp3OCDriLuD!DZI>5!8GX@HHoO=OxddBgO*b~(h+!doy&9j{oJi)bQ z5$CZ1{bC|otQt9OT;?rtEgEn!peK?5Zi~XIsu)9HC_sECWSSj3>J^vi6(`P-q9Ade zOk#bNHoS+`4zTJyY+f#}&N8D;K1CK>V?IADcnfbZ*BEmZ5W}2CzcWlOtD-`g?gX#( zbhVt$#p&z{UW4xfIeQWxV)J>L28@$;LW+rG6jB)LfFEWwOb34gsEn1Bdep_e>S; zp#bj??{k`|2J49@k?T`dbaSNcMUX;_>76}gc#!^|KGL@r@vC)Ydi9%*Cj3l>dH2;Lfu_oe4L`<>sXjd#GTmEVSZt_GCiV!wUZv9w=8}ZZrQb%?3&&q zMfQj}d0ruPu#axL{uDicAubu>_hpDqG7L)!ew+-7Izxm($ gUPnE&QLY~~P(60DoNqDyGt8_%@7t)Cwks(84|-`(W&i*H From 805633d678e27b86d5f0d80d84136f39a79ecdec Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Sun, 10 May 2026 21:33:44 +0100 Subject: [PATCH 15/47] fix: remove unused import --- .../service/ReservationService.java | 5 ++--- .../service/ReservationService.class | Bin 11010 -> 11010 bytes 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 92aea82..3cd4964 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -5,7 +5,6 @@ import java.util.List; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; @@ -13,13 +12,13 @@ import com.pachedev.restoreserve.dto.ReservationResponseDTO; import com.pachedev.restoreserve.exception.BusinessLogicException; import com.pachedev.restoreserve.exception.ResourceNotFoundException; +import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.entity.Reservation; import com.pachedev.restoreserve.model.entity.RestaurantTable; -import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.enums.ReservationStatus; +import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.repository.ReservationRepository; import com.pachedev.restoreserve.repository.RestaurantTableRepository; -import com.pachedev.restoreserve.repository.AppUserRepository; import lombok.RequiredArgsConstructor; diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index dbb5d60a56a3f4aabe1bc5a6065f1066e8c4c630..ef4c5509794ec1300e6e153659cba347168623bc 100644 GIT binary patch delta 354 zcmWNM&nv@m9L8VY!^)55$Av7^Z2NBC#h9N%wlqqU=I3ffN|Gfk!qn0>imV;@9JVMe z2NzZ(%7vSwl5+MpI68UvK0KG_{XEYr6UjvG6V|8=Gj7_^?H~6@tpC<-*WsrA<_$Ht}tzYIK)r`UY-;SkkXlR@k9vLQj65)@~i zE%jsnsNI$DgjDX!{Dgoc)tlqloT7p>b)=;7P-XO-`B=bD#FrJ+oN9EQn$D<1sat2M zDKGAVxbxz^z{;f{UkPGSV6N4U>qxVxKUY$-t36{_bE+4^r^VE-;n&j2>-K8}6?dQi E1AOR9=l}o! delta 354 zcmWNM$t#3$7{@>FVan2Lxsb8cFxxxUv1DyfLo${jrihf1W)h=GgHYZa?1%X&Gf_yn zFh!zVxH&2*XMcmElc!%jzvu9IzKa<$L-(t;2|GFtim+qFCC^QR%tk!)P{}YgOjFA& zb%gN}qn<4xl>DbOb4d#~v~ouqPqgz&2k-d!rb~U=zH~piqEkjMPW=g5oej6Qgu8Mjzv{g=7YqVe;{yqDnL7$Yh>831B56VZ$P}AfXoJWQl&F46@7= zG4*5r$d8Nonpmz&g*7R%soosV`ZigZ6tFFpJIZt(>AO;VPq;%2j#Y_k-;yAk6LsrK zmnMZy37r)B3>)WC_d;r3;pbYdxOcQ-_2=Hu94c;fYi{*ov}h6aYxp!%c|ATYrJ|n1 EKea+kHvj+t From edfb3cd0b0b812825ac1b808fb1ff62a00e12e7d Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Sun, 10 May 2026 21:57:35 +0100 Subject: [PATCH 16/47] feat: add register endpoint --- .../New Request.request.yaml | 4 +++ .../controller/AuthController.java | 7 +++++ .../restoreserve/service/AuthService.java | 27 ++++++++++++++++++ .../controller/AuthController.class | Bin 1736 -> 2618 bytes .../restoreserve/service/AuthService.class | Bin 3542 -> 4943 bytes 5 files changed, 38 insertions(+) create mode 100644 postman/collections/RestoReserve Collection/New Request.request.yaml diff --git a/postman/collections/RestoReserve Collection/New Request.request.yaml b/postman/collections/RestoReserve Collection/New Request.request.yaml new file mode 100644 index 0000000..230efdc --- /dev/null +++ b/postman/collections/RestoReserve Collection/New Request.request.yaml @@ -0,0 +1,4 @@ +$kind: http-request +url: "" +method: GET +order: 4497869906067502 diff --git a/src/main/java/com/pachedev/restoreserve/controller/AuthController.java b/src/main/java/com/pachedev/restoreserve/controller/AuthController.java index 5288add..696678a 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/AuthController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/AuthController.java @@ -5,11 +5,13 @@ import com.pachedev.restoreserve.dto.AuthRequestDTO; import com.pachedev.restoreserve.dto.AuthResponseDTO; +import com.pachedev.restoreserve.dto.RegisterUserRequestDTO; import com.pachedev.restoreserve.service.AuthService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -26,4 +28,9 @@ public ResponseEntity login(@Valid @RequestBody AuthRequestDTO return ResponseEntity.ok(authService.login(dto)); } + @PostMapping("/register") + public ResponseEntity register(@Valid @RequestBody RegisterUserRequestDTO dto) { + return ResponseEntity.status(HttpStatus.CREATED).body(authService.register(dto)); + } + } diff --git a/src/main/java/com/pachedev/restoreserve/service/AuthService.java b/src/main/java/com/pachedev/restoreserve/service/AuthService.java index ad606d8..8b559b9 100644 --- a/src/main/java/com/pachedev/restoreserve/service/AuthService.java +++ b/src/main/java/com/pachedev/restoreserve/service/AuthService.java @@ -5,9 +5,11 @@ import com.pachedev.restoreserve.dto.AuthRequestDTO; import com.pachedev.restoreserve.dto.AuthResponseDTO; +import com.pachedev.restoreserve.dto.RegisterUserRequestDTO; import com.pachedev.restoreserve.exception.BusinessLogicException; import com.pachedev.restoreserve.exception.ResourceNotFoundException; import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.model.enums.UserRole; import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.security.JwtService; @@ -46,4 +48,29 @@ public AuthResponseDTO login(AuthRequestDTO dto) { return new AuthResponseDTO(token); } + public String register(RegisterUserRequestDTO dto) { + + if (appUserRepository.existsByUsername(dto.username())) { + throw new BusinessLogicException( + "El nombre de usuario " + dto.username() + " ya está registrado. Elija otro."); + } + + if (appUserRepository.existsByEmail(dto.email())) { + throw new BusinessLogicException("El email " + dto.email() + " ya está registrado. Introduzca otro."); + } + + AppUser user = new AppUser(); + user.setName(dto.name()); + user.setUsername(dto.username()); + user.setTelephone(dto.telephone()); + user.setEmail(dto.email()); + user.setPassword(passwordEncoder.encode(dto.password())); + user.setRole(UserRole.ROLE_USER); + user.setActive(true); + + appUserRepository.save(user); + + return "El usuario ha sido registrado correctamente"; + } + } diff --git a/target/classes/com/pachedev/restoreserve/controller/AuthController.class b/target/classes/com/pachedev/restoreserve/controller/AuthController.class index 17cc9fed9fdd65492da3b5b0ec66968b09758583..f975a45332edf9df9a751514c07caa190b4e6b17 100644 GIT binary patch delta 723 zcmZ`%T~8BH5IuKydrRq7Yg;ga306?c7Tsc@3M++nTPTPjDfnb!T+^Fc(-hec6QB46 zKA8JIh#2vM2cM0!qtbK7@2y|z^* zaLoL}8N*GOQQTsflochDedhZw%m%q_-|+)aM**`8QM*~M?9^)v*W`*aa^n=r8pb$^ zI_5CXz`cGa!;@Ty9XW-ycU!`m4lDAtnjW93?zUc4gHE?4T%sAp9UVE`W!UKJnIDwe z6nla2{MyczHNAO8R~(DD7X_&+hDy$`>$Khp2JI;gWGID;y!an>xu*rZrehg(`CU7p z1?cO&o^b81<9Whkh!1=JG^Bkdcvcw}W z4|$xYT%1ukNtX*`DY6F9DRgGuVdfKV8}E_Me?aLo78Jatyn-v_InjWIXUUJA;Bgg` z6j3LpP^S8I$QeGiu#6R=tWY6rs?r&bDN|E8X4v=$+sGdweMr$Dc}DK@+^mY#etxa5 YAVr7<#p{1nY~X&#Lh&L!gH0a$1%1|*=Kufz delta 171 zcmdlba)MXj)W2Q(7#J9A8BEw2m>3z1Cr@OOn*5eoa-*{W%VcTxVm4hK1`P(i$y3ob_ diff --git a/target/classes/com/pachedev/restoreserve/service/AuthService.class b/target/classes/com/pachedev/restoreserve/service/AuthService.class index d12c6c33c11d69443d1ffccc818e0e9b7734a8ec..343a19ad2202ba399a8d54b01eae279c036691b7 100644 GIT binary patch delta 1685 zcmZ`(TUQ%Z6#h;KnUD-hF$IB&(}h9^k(8x^tB4k8s%3>zLjj3Z9Fi#nk{LQPp`~b} zZEbD6J62n*mwK-kl!~E%wraiBOVtOLk1l_NPcE1H1gLqyhdKM4y}$kK%h_jstGHNQ zbm5=*vjAG~s{k9rbyh1YpY|Peh)!J@iW{b)Gjv$fjy2^mQwxNbbUlWmhn1Zv#V|K+ z>$2|1_qbiS3s(r(&Ts>9R5q;)b)yvbh#0`V3`NRr3ShKNa5hy=Dh#W=);hZy%4T2O9HyON%BpGbc#4P}Sj~{HB;|O5a}SG%bMC@r?jW}m!-hc@ zs*w-r8PHpotjc1a=;t19TAv(k%>vp_p5ilqnJ8Af20Z%eivz-6AHqcY&!S zl(At=%~DK?xR0YnLT}48>=lvb1}7P^X>pD^HyW@{#C{xLa42e2izyWhes5?*9+v|N zSse3wY=L3;m(Si5X!chUw zFs%Bogf@*FWmUwpc#fFH!yIP}We4GN88#M~H)YRSNuwTYFLt~}+<)|BHyFxILrdk8;D*I0i*Ybp1 z*Mu}}G&LsRXKTvc$?VqOZoRAqh47*bH7JJ<^{B&5v~HxyJS2wFccPyja3R3fQ2+Ai zcXiA+gUjkNh|J+`8;--XfX04)o`(#~V*R-#Kx_^}xq#R_Miv1JsP3P|Xs4gt)C_iV z8Ogxpa#sd>xQu0Rzu%XhftL;s%;7;BGI+>;8V_gixZi#TPiGcW@z3Fe4SmJ0d`QXV zAw+8%ifFx&)<$^HgsVud#d><}&1Aa;9k>-?Y(NC97$nUI+A&Eb??Vtra2t+dGdhVu zLL^%;wxElSR6DlfRT_p4(uC1Xo#`d59k#U+`e>KW2td7}KizAT%j>jv(CH15)Iw}0 z2RliRZx(M_e~DMKE3J~kO=%h|LUBX`c`=f!7I3QHmqF1y-sL@ej~8dl7RL!eH^vjR y(Pzi|+1(UAAjz#9J566D+0GE*JU+!ad_|V8v+K9n^#Xn%3?FUbM_j}&DEk+^@v*o7 delta 460 zcmYk2Pfrt36vcn9ZDz2}l#vj%m=HHpXNCeXaiI}S5JNDjx>He9hEo0%C`w0EREnaw zH1TfS`4wbQNifm9F>&WsK7~v44l5V;p7YMlIq&BFEd4s+ec#{x3S8i=O~%kK!=`6D z+Op(he$Sb37~-xW8-zDx<8aHw@10A&q3STrnBmw`yigliZdBvs=6o`zteM1(q?u<@ z#+-F)k%rBZ{BTZXmw6z6oV{?t&>e)8db}DBF2;@7!Ru4?S~WTEv69E|$l)IMJ1r9V z>y93JoM(;aHqYc*A+)D?VfYZpuzOrix>w|P;au)%2OILPvoE)4@TFDoEt@xnUZuTa zdu5ZAGu}$k$E8FOpB@4R2sulc%M8djuQD>kEwYU39W349wxX*wL57@SceKNusIN^S zDz^!u{x%o4RnJh-o;u*DY<7*V>FWAQy{9!V)sK8(ay Date: Sun, 10 May 2026 22:14:31 +0100 Subject: [PATCH 17/47] fix: use jwt user for reservation creation --- .../New Request.request.yaml | 4 ---- .../Register new user.request.yaml | 15 +++++++++++++++ .../controller/ReservationController.java | 7 ++++++- .../dto/ReservationRequestDTO.java | 7 +++---- .../service/ReservationService.java | 6 +++--- .../controller/ReservationController.class | Bin 4554 -> 5041 bytes .../dto/ReservationRequestDTO.class | Bin 3013 -> 2741 bytes .../service/ReservationService.class | Bin 11010 -> 11211 bytes 8 files changed, 27 insertions(+), 12 deletions(-) delete mode 100644 postman/collections/RestoReserve Collection/New Request.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Register new user.request.yaml diff --git a/postman/collections/RestoReserve Collection/New Request.request.yaml b/postman/collections/RestoReserve Collection/New Request.request.yaml deleted file mode 100644 index 230efdc..0000000 --- a/postman/collections/RestoReserve Collection/New Request.request.yaml +++ /dev/null @@ -1,4 +0,0 @@ -$kind: http-request -url: "" -method: GET -order: 4497869906067502 diff --git a/postman/collections/RestoReserve Collection/Register new user.request.yaml b/postman/collections/RestoReserve Collection/Register new user.request.yaml new file mode 100644 index 0000000..b14fb3a --- /dev/null +++ b/postman/collections/RestoReserve Collection/Register new user.request.yaml @@ -0,0 +1,15 @@ +$kind: http-request +name: Register new user +url: http://localhost:8080/api/v1/auth/register +method: POST +body: + type: json + content: |- + { + "name": "Tanausú Febles", + "username": "ertana", + "telephone": "600000000", + "email": "tana@gmail.com", + "password": "1234" + } +order: 4497869906067502 diff --git a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java index 256659d..db60ed0 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java @@ -4,6 +4,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -29,7 +30,11 @@ public class ReservationController { @PostMapping public ResponseEntity create(@Valid @RequestBody ReservationRequestDTO dto) { - ReservationResponseDTO response = reservationService.create(dto); + + // Obtenemos el usuario del contexto de seguridad (JWT) + String currentUsername = SecurityContextHolder.getContext().getAuthentication().getName(); + + ReservationResponseDTO response = reservationService.create(dto, currentUsername); return ResponseEntity.status(HttpStatus.CREATED).body(response); } diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java index 4ecb74c..acf36d6 100644 --- a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java @@ -7,8 +7,7 @@ import jakarta.validation.constraints.NotNull; public record ReservationRequestDTO( - @NotNull(message = "el id del usuario es obligatorio") Long userId, - @NotNull(message = "el id de la mesa es obligatorio") Long tableId, - @NotNull(message = "la fecha de reserva es obligatoria") @Future(message = "La reserva debe ser en una fecha futura") LocalDateTime reservationDate, - @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests) { + @NotNull(message = "el id de la mesa es obligatorio") Long tableId, + @NotNull(message = "la fecha de reserva es obligatoria") @Future(message = "La reserva debe ser en una fecha futura") LocalDateTime reservationDate, + @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests) { } diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 3cd4964..57cb528 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -30,10 +30,10 @@ public class ReservationService { private final RestaurantTableRepository tableRepository; private final AppUserRepository appUserRepository; - public ReservationResponseDTO create(ReservationRequestDTO dto) { + public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUsername) { - AppUser user = appUserRepository.findById(dto.userId()) - .orElseThrow(() -> new ResourceNotFoundException("Usuario con id " + dto.userId() + " no encontrado")); + AppUser user = appUserRepository.findByUsername(currentUsername) + .orElseThrow(() -> new ResourceNotFoundException("Usuario " + currentUsername + " no encontrado")); RestaurantTable table = tableRepository.findById(dto.tableId()) .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + dto.tableId() + " no encontrada")); diff --git a/target/classes/com/pachedev/restoreserve/controller/ReservationController.class b/target/classes/com/pachedev/restoreserve/controller/ReservationController.class index df89e1eb00725a27f967c98afe0d2e4792a7f7fa..7bde94c9888ecaad408c54ccac1b40813b7ad5cf 100644 GIT binary patch delta 1621 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 AeEsgM0X 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 FF98 Date: Mon, 11 May 2026 20:07:01 +0100 Subject: [PATCH 18/47] fix: restrict reservations access by authenticated user --- .../service/ReservationService.java | 35 ++++++++++++++++-- .../service/ReservationService.class | Bin 11211 -> 11970 bytes 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 57cb528..0fce8af 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -125,8 +125,11 @@ private List toResponseList(List reservatio } public ReservationResponseDTO findById(Long id) { - Reservation reservation = reservationRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); + Reservation reservation = getReservation(id); + + AppUser user = getUser(); + + validateReservationAccess(reservation, user); return toResponseDTO(reservation); } @@ -148,8 +151,11 @@ public List findByStatus(ReservationStatus status) { } public void cancelReservation(Long id) { - Reservation reservation = reservationRepository.findById(id) - .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); + Reservation reservation = getReservation(id); + + AppUser user = getUser(); + + validateReservationAccess(reservation, user); if (reservation.getStatus() == ReservationStatus.CANCELLED) { throw new BusinessLogicException("La reserva con id " + id + " ya estaba cancelada"); @@ -160,4 +166,25 @@ public void cancelReservation(Long id) { reservationRepository.save(reservation); } + + private void validateReservationAccess(Reservation reservation, AppUser user) { + if (!user.getRole().name().equals("ROLE_ADMIN") && !reservation.getUser().getId().equals(user.getId())) { + throw new BusinessLogicException("Su usuario no tiene permisos para acceder a la reserva especificada"); + } + } + + private AppUser getUser() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = authentication.getName(); + + AppUser user = appUserRepository.findByUsername(username) + .orElseThrow(() -> new ResourceNotFoundException("Usuario " + username + " no encontrado")); + return user; + } + + private Reservation getReservation(Long id) { + Reservation reservation = reservationRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); + return reservation; + } } diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index 67edb7553e47179122d23b5554a4da9ea15a3175..cc7348055015b361a1613636b658f29bc6d051fc 100644 GIT binary patch delta 1954 zcmZ`)T~L%|6n>svT-ar$1+8V*1wIDlXLpfM$_$YJH8r4=(ky>kVX;*NWWm&|4HYv@ zQ(yB3|I;+hDe-4jG_$&^%VwIoX}W2~i>3>wt7d9C@5j3AOm(sE?|II7-sd~#?01X5 zUgrAt@8P=uYIr^!7NSZOTD?R5es{hDWQCKDvy#YK74BUaj)r^Ihk7Gjox~i~>MmFJ z(p=LXo>hxIhBTo?A8OJ@yHqZz8teMMP0NMo%NYxoRNT&fmi6k9k${ha&2 z_NY5v7H#W|mm4h*?b>MeCn!yD`Lrb607wrr6M7oU5 zE~7UR?hG5<;hwdTXjjze4)uhLkT7ox_ZT6gBV_c9Xkmn--Qm_qd!#kg7RrGQ?o8}P zc@EO~k`uXHM5sINDZHUyyKisd)ri+k)~fyqJ7jeD7O+h%^%c2t;9xD0(Ggm^vMn^R zO2vGOteIkR-?!D)&7O3wQ|t0dtWox=qj|qncDeAAR(gYzuX5=lHooEF%EZ}QPA=yP zIom8}4)$|vI=84x`2pKD;&5rB`pTH3zR#;xH3iet-pSy*YE!{{*G?xxTq&sc1eL)( z>V83?HG_MVVfX_H&&tWL8EyY)w8KuW;_HGtWa5sgCd0=QYPDhTl-gxX;Td(;@TYxD zaH`L5#o68A}2;v0|7rfg2XqsV_n( zylA~@|D{(jj&H8;xpVRc)F}C0}K@sYh#5^H#ox%j1Mi6H)73VM$ zpQH#}o8(cO6nJ`4;Hrec7lcP9_!oldXi0~Ws<=g0e}eO7;jG_Y9t*~4k8|ue=EOOE z5CwraF30JWV{e?#4#W+!XyWrs@9GA!?@*Bxji|(+p z)0|pm)^d3gsnTLWV3;o^IJ7hcM>$CE6T@LRa2t=w?8coGbv#LRJR@JRS`4%hx=vE7 z19Q0;R=zC0Y*H&rWY$EMikbF5-hYFys0(H7hV+?)aFeyRa+jqG#y`wul4(*b6@Fi_ zG@gV}Rl>-uDt{ak&BA(Z#B`nCmnYpOVHz->EcvAUjbp2JcG2&PNw4{zi0!7EQ7weI z@VRcj-vYQCx+f*Dl?fu*CR>w5*a8=PxM;P zjof6e-sIbiaX@UgoBJKy#rMtqZgc#=Jl)3w{Low-W zL?#G`2e&a2VqBup#6-jloKgI_F?#2fUYHmY@4UiWV|>5EXBmcGyyu+vJkNRFzpvl! z{;|*Z!#|5(0chf7HylJvg`I_0BNKt#9Gpc>9o`Z=)dj}F_u{`7mG8&q64RF7kgX%`;0p|fet9}ptR=>}OU%Vg>8r;gq zb6p(ral7q%-ry7L72k;XygbYo-F!j)RZ_(xs>HmKHSXcd>Kk)Q*)fCLxLts+-XTvK z+`*mVJ0U&~PpP7CIAA|~+TbpI_lCYZr<%hN)5Eujz_zi&iP2=rq&b{22Z#5ihQ~(} z11S&RR#(F&FRGuy4Ouh9YE@ejP$i{4UQ%_XUCh09tn}w>^8g%hO6!7=&v3fxbJjXg zwa6&2fWoiQ+oeV-0%>Rg9j@WRsTKuXz z5=zgA`mB_BM#?ycFfOA3SFjH6q66#K+)d zrvO~ik}g@Tz;{UkS+cH(IIpu?%~$U?WvZo>)@7|^MOl|yjfpI>M|VY4))be^nA@P3 z4bt6YqNk<}_$LPmM7?E7IbDW!al zhd8WvDUNcCM+NheKEJ{=kL%q8Uu)q>ef7F_Oz{lg)aSF>p4aw*w#v5eh=}zMGtF7f Gq2ON`yY~bD From e80639c5db3e7f8e8fabcd97504a66c39c43f147 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 11 May 2026 20:31:34 +0100 Subject: [PATCH 19/47] refactor: remove userId reservation endpoint and fix findByStatus filtering --- .../controller/ReservationController.java | 5 ---- .../repository/ReservationRepository.java | 2 ++ .../service/ReservationService.java | 25 +++++++----------- .../controller/ReservationController.class | Bin 5041 -> 4687 bytes .../repository/ReservationRepository.class | Bin 1154 -> 1214 bytes .../service/ReservationService.class | Bin 11970 -> 11491 bytes 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java index db60ed0..90bec25 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java @@ -49,11 +49,6 @@ public ResponseEntity findById(@PathVariable Long id) { return ResponseEntity.ok(reservationService.findById(id)); } - @GetMapping("/user/{userId}") - public ResponseEntity> findByUserId(@PathVariable Long userId) { - return ResponseEntity.ok(reservationService.findByUserId(userId)); - } - @GetMapping("/status/{status}") public ResponseEntity> findByStatus(@PathVariable ReservationStatus status) { return ResponseEntity.ok(reservationService.findByStatus(status)); diff --git a/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java b/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java index 6b8736b..6317bc7 100644 --- a/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java +++ b/src/main/java/com/pachedev/restoreserve/repository/ReservationRepository.java @@ -14,4 +14,6 @@ public interface ReservationRepository extends JpaRepository List findByStatus(ReservationStatus status); List findByTableIdAndStatus(Long tableId, ReservationStatus status); + + List findByUserIdAndStatus(Long id, ReservationStatus status); } diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 0fce8af..630bcbf 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -97,12 +97,7 @@ private Reservation findConflictingReservation(Long tableId, LocalDateTime reque */ public List findAll() { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - - String username = authentication.getName(); - - AppUser user = appUserRepository.findByUsername(username) - .orElseThrow(() -> new ResourceNotFoundException("Usuario no encontrado")); + AppUser user = getUser(); List reservations = new ArrayList<>(); @@ -134,18 +129,16 @@ public ReservationResponseDTO findById(Long id) { return toResponseDTO(reservation); } - public List findByUserId(Long userId) { - if (!appUserRepository.existsById(userId)) { - throw new ResourceNotFoundException("Usuario con id " + userId + " no encontrado"); - } - - List reservations = reservationRepository.findByUserId(userId); + public List findByStatus(ReservationStatus status) { + AppUser user = getUser(); - return toResponseList(reservations); - } + List reservations = new ArrayList<>(); - public List findByStatus(ReservationStatus status) { - List reservations = reservationRepository.findByStatus(status); + if (user.getRole().name().equals("ROLE_ADMIN")) { + reservations = reservationRepository.findByStatus(status); + } else { + reservations = reservationRepository.findByUserIdAndStatus(user.getId(), status); + } return toResponseList(reservations); } diff --git a/target/classes/com/pachedev/restoreserve/controller/ReservationController.class b/target/classes/com/pachedev/restoreserve/controller/ReservationController.class index 7bde94c9888ecaad408c54ccac1b40813b7ad5cf..ab0fff8a57daf221f07b5883ea9e70a8daeaee31 100644 GIT binary patch delta 345 zcmX|7%}N4c6g}7Qs~M-s8MpmFMV1B`Oe|o?_6#p{57n8G+*BUq;S_zv#L8Em?W{GVHKNhT^py7 zT@BkP5JIDwf>pC(Ma!{cnUZ5C$5vEKpr~OVj{NRUD`gyZ;(#zEEegv;-Jpuh>G$GP zLltN8S8tMVe|jEhU=4mKoa#dk0Y)8!G}m$#RKdE8nuBD?Pa_(MV~h0#_V@{L2I6&= zyz#s@Zp%KSSH7FU{+(y+y`V&RY$wccS2+ntq6;bcXGVsqI6?<%tO@?`m@&vT%dVDB pjXdE*PKV|a=gfS#Xor~mx@3Ok?Rkwl^Om0AL)-`jWyV{f`~x!_K9m3e delta 497 zcmZutOD{uV5IsYBxnHm9B@z-LLF(1yMj|XqsRa>DEQC$HF0Cq6kG6`UUai;8_Xqq0 z(uK#uo{g;^VdV#iX)C(1m^t4}&N(yly|>)Ains6Q7XbaZx)(m7PDTS;qkckKdNhw5 zunyPTj5zmN1D_s*D7?{lc|b~ za-<=NV`;U0Q(0D2-csWPEKvD^63oKNXaix4W6}qTVotu=n`mBs+B>@!G0puE=2!@e z48*Gi{^U#l$b&g~&GIA0@A4!1I4i6JTsL}M+iKCTJMR}pahlwQ!f-13C2rB{Q+mUT_XSh diff --git a/target/classes/com/pachedev/restoreserve/repository/ReservationRepository.class b/target/classes/com/pachedev/restoreserve/repository/ReservationRepository.class index f9dfe31f3081cfd5196d82d3670a474f7c468155..6bb8164be673372a1ad2668305fd605b4e82c31a 100644 GIT binary patch delta 116 zcmZqT+{Yff$?3=9mm3{vb2OpFXxlNlMMHfm`yONpjs=A}4Qh8Cw5d8Rn#r39BG zmXsDVGB9OM4q!gT$TC@)CDf3GkwKI}h=GZL6KDn#gD?XRkmh7!5Cc*IU?FiZDFIa= F2>>Z_6Po}4 delta 58 zcmdnT*~BSu>ff$?3=9mm3=-@NOpFXxlNlMMHfm`yPcC3S&d5C3f+bXlfr&vBsDz0@ KjDZtOivs|e6$(lK diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index cc7348055015b361a1613636b658f29bc6d051fc..88eef1e2da70af0b3fe0c53a5708c2c8c55531f5 100644 GIT binary patch delta 2165 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 From c603108647559ec65f94bb2e72b36cde916c22a0 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 11 May 2026 21:07:46 +0100 Subject: [PATCH 20/47] docs: update postman collection --- ... a Reservation (soft delete).request.yaml} | 2 +- .../Create a new reservation.request.yaml | 18 ++++++++++++++++++ ...ate a overlapping reservation.request.yaml | 18 ++++++++++++++++++ .../Create a past reservation.request.yaml | 19 +++++++++++++++++++ ...tion exceeding table capacity.request.yaml | 18 ++++++++++++++++++ .../Creates a new reservation-1.request.yaml | 15 --------------- .../Get a reservation by its id.request.yaml | 12 ++++++++++++ .../Get reservations by status.request.yaml | 9 +++++++++ .../Get reservations.request.yaml | 13 +++++++++++++ .../Get tables.request.yaml | 5 +++++ .../Gets a reservation by its id.request.yaml | 5 ----- .../Gets all reservations.request.yaml | 5 ----- ...s reservation list by User Id.request.yaml | 5 ----- .../Gets reservations by status.request.yaml | 5 ----- .../Login Admin.request.yaml | 12 ++++++++++++ .../Login user.request.yaml | 2 +- .../Register new user.request.yaml | 2 +- 17 files changed, 127 insertions(+), 38 deletions(-) rename postman/collections/RestoReserve Collection/{Cancels a Reservation (soft delete).request.yaml => Cancel a Reservation (soft delete).request.yaml} (72%) create mode 100644 postman/collections/RestoReserve Collection/Create a new reservation.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Create a overlapping reservation.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Create a past reservation.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Create a reservation exceeding table capacity.request.yaml delete mode 100644 postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Get a reservation by its id.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Get reservations by status.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Get reservations.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Get tables.request.yaml delete mode 100644 postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml delete mode 100644 postman/collections/RestoReserve Collection/Gets all reservations.request.yaml delete mode 100644 postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml delete mode 100644 postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml create mode 100644 postman/collections/RestoReserve Collection/Login Admin.request.yaml diff --git a/postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml b/postman/collections/RestoReserve Collection/Cancel a Reservation (soft delete).request.yaml similarity index 72% rename from postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml rename to postman/collections/RestoReserve Collection/Cancel a Reservation (soft delete).request.yaml index 1e59e52..d5b905f 100644 --- a/postman/collections/RestoReserve Collection/Cancels a Reservation (soft delete).request.yaml +++ b/postman/collections/RestoReserve Collection/Cancel a Reservation (soft delete).request.yaml @@ -1,5 +1,5 @@ $kind: http-request -name: Cancels a Reservation (soft delete) +name: Cancel a Reservation (soft delete) url: http://localhost:8080/api/v1/reservations/1 method: DELETE order: 4441617885660404 diff --git a/postman/collections/RestoReserve Collection/Create a new reservation.request.yaml b/postman/collections/RestoReserve Collection/Create a new reservation.request.yaml new file mode 100644 index 0000000..7e4d944 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Create a new reservation.request.yaml @@ -0,0 +1,18 @@ +$kind: http-request +name: Create a new reservation +description: Get all reservations +url: http://localhost:8080/api/v1/reservations +method: POST +body: + type: json + content: |- + { + "tableId": 1, + "reservationDate": "2026-05-20T20:00:00", + "numberOfGuests": 2 + } +auth: + type: bearer + credentials: + token: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhcm15bW92ZXMiLCJpYXQiOjE3Nzg1Mjg3MDQsImV4cCI6MTc3ODYxNTEwNH0.skbvHYXliP3y5kspCJGfMsWX5QCZ5UN7JoEvcNqj7rUn6E_Hsu_fypRswr3j8C53 +order: 178851836358 diff --git a/postman/collections/RestoReserve Collection/Create a overlapping reservation.request.yaml b/postman/collections/RestoReserve Collection/Create a overlapping reservation.request.yaml new file mode 100644 index 0000000..58a5569 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Create a overlapping reservation.request.yaml @@ -0,0 +1,18 @@ +$kind: http-request +name: Create a overlapping reservation +description: Get all reservations +url: http://localhost:8080/api/v1/reservations +method: POST +body: + type: json + content: |- + { + "tableId": 2, + "reservationDate": "2026-05-16T15:00:00", + "numberOfGuests": 2 + } +auth: + type: bearer + credentials: + token: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhcm15bW92ZXMiLCJpYXQiOjE3Nzg1Mjg3MDQsImV4cCI6MTc3ODYxNTEwNH0.skbvHYXliP3y5kspCJGfMsWX5QCZ5UN7JoEvcNqj7rUn6E_Hsu_fypRswr3j8C53 +order: 1067421965123421 diff --git a/postman/collections/RestoReserve Collection/Create a past reservation.request.yaml b/postman/collections/RestoReserve Collection/Create a past reservation.request.yaml new file mode 100644 index 0000000..258b191 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Create a past reservation.request.yaml @@ -0,0 +1,19 @@ +$kind: http-request +name: Create a past reservation +description: Get all reservations +url: http://localhost:8080/api/v1/reservations +method: POST +body: + type: json + content: |- + { + "userId": 1, + "tableId": 3, + "reservationDate": "2026-03-10T22:00:00", + "numberOfGuests": 4 + } +auth: + type: bearer + credentials: + token: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhcm15bW92ZXMiLCJpYXQiOjE3Nzg1Mjg3MDQsImV4cCI6MTc3ODYxNTEwNH0.skbvHYXliP3y5kspCJGfMsWX5QCZ5UN7JoEvcNqj7rUn6E_Hsu_fypRswr3j8C53 +order: 950741626747683 diff --git a/postman/collections/RestoReserve Collection/Create a reservation exceeding table capacity.request.yaml b/postman/collections/RestoReserve Collection/Create a reservation exceeding table capacity.request.yaml new file mode 100644 index 0000000..2f84679 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Create a reservation exceeding table capacity.request.yaml @@ -0,0 +1,18 @@ +$kind: http-request +name: Create a reservation exceeding table capacity +description: Get all reservations +url: http://localhost:8080/api/v1/reservations +method: POST +body: + type: json + content: |- + { + "tableId": 2, + "reservationDate": "2026-05-16T15:00:00", + "numberOfGuests": 4 + } +auth: + credentials: + token: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhcm15bW92ZXMiLCJpYXQiOjE3Nzg1Mjg3MDQsImV4cCI6MTc3ODYxNTEwNH0.skbvHYXliP3y5kspCJGfMsWX5QCZ5UN7JoEvcNqj7rUn6E_Hsu_fypRswr3j8C53 + type: bearer +order: 1263142336234000 diff --git a/postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml b/postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml deleted file mode 100644 index 1a55640..0000000 --- a/postman/collections/RestoReserve Collection/Creates a new reservation-1.request.yaml +++ /dev/null @@ -1,15 +0,0 @@ -$kind: http-request -name: Creates a new reservation -description: Get all reservations -url: http://localhost:8080/api/v1/reservations -method: POST -body: - type: json - content: |- - { - "userId": 1, - "tableId": 3, - "reservationDate": "2026-05-10T22:00:00", - "numberOfGuests": 4 - } -order: 178851836358 diff --git a/postman/collections/RestoReserve Collection/Get a reservation by its id.request.yaml b/postman/collections/RestoReserve Collection/Get a reservation by its id.request.yaml new file mode 100644 index 0000000..392dff6 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Get a reservation by its id.request.yaml @@ -0,0 +1,12 @@ +$kind: http-request +name: Get a reservation by its id +url: http://localhost:8080/api/v1/reservations/2 +method: GET +body: + type: json + content: "" +auth: + type: bearer + credentials: + token: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhcm15bW92ZXMiLCJpYXQiOjE3Nzg1Mjg3MDQsImV4cCI6MTc3ODYxNTEwNH0.skbvHYXliP3y5kspCJGfMsWX5QCZ5UN7JoEvcNqj7rUn6E_Hsu_fypRswr3j8C53 +order: 1442191939044361 diff --git a/postman/collections/RestoReserve Collection/Get reservations by status.request.yaml b/postman/collections/RestoReserve Collection/Get reservations by status.request.yaml new file mode 100644 index 0000000..026e0f8 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Get reservations by status.request.yaml @@ -0,0 +1,9 @@ +$kind: http-request +name: Get reservations by status +url: http://localhost:8080/api/v1/reservations/status/CONFIRMED +method: GET +auth: + type: bearer + credentials: + token: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhcm15bW92ZXMiLCJpYXQiOjE3Nzg1Mjg3MDQsImV4cCI6MTc3ODYxNTEwNH0.skbvHYXliP3y5kspCJGfMsWX5QCZ5UN7JoEvcNqj7rUn6E_Hsu_fypRswr3j8C53 +order: 4411268052689732 diff --git a/postman/collections/RestoReserve Collection/Get reservations.request.yaml b/postman/collections/RestoReserve Collection/Get reservations.request.yaml new file mode 100644 index 0000000..546f0d1 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Get reservations.request.yaml @@ -0,0 +1,13 @@ +$kind: http-request +name: Get reservations +url: http://localhost:8080/api/v1/reservations +method: GET +body: + type: json + content: "" +auth: + credentials: + - key: token + value: eyJhbGciOiJIUzM4NCJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTc3ODUyODQ0OCwiZXhwIjoxNzc4NjE0ODQ4fQ.JRtbYGO9erQNUcgnmVtOvrSD-TbA81Unf8kYnUtB7rhEcZkjkasQnF5AEPYNCAZY + type: bearer +order: 169061786500 diff --git a/postman/collections/RestoReserve Collection/Get tables.request.yaml b/postman/collections/RestoReserve Collection/Get tables.request.yaml new file mode 100644 index 0000000..cbfbaa0 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Get tables.request.yaml @@ -0,0 +1,5 @@ +$kind: http-request +name: Get tables +url: "" +method: GET +order: 4477808826848290 diff --git a/postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml b/postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml deleted file mode 100644 index da526fd..0000000 --- a/postman/collections/RestoReserve Collection/Gets a reservation by its id.request.yaml +++ /dev/null @@ -1,5 +0,0 @@ -$kind: http-request -name: Gets a reservation by its id -url: http://localhost:8080/api/v1/reservations/1 -method: GET -order: 1442191939044361 diff --git a/postman/collections/RestoReserve Collection/Gets all reservations.request.yaml b/postman/collections/RestoReserve Collection/Gets all reservations.request.yaml deleted file mode 100644 index ad1bc70..0000000 --- a/postman/collections/RestoReserve Collection/Gets all reservations.request.yaml +++ /dev/null @@ -1,5 +0,0 @@ -$kind: http-request -name: Gets all reservations -url: http://localhost:8080/api/v1/reservations -method: GET -order: 228234296794 diff --git a/postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml b/postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml deleted file mode 100644 index 18ef6b2..0000000 --- a/postman/collections/RestoReserve Collection/Gets reservation list by User Id.request.yaml +++ /dev/null @@ -1,5 +0,0 @@ -$kind: http-request -name: Gets reservation list by User Id -url: http://localhost:8080/api/v1/reservations/user/1 -method: GET -order: 3809538882598051 diff --git a/postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml b/postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml deleted file mode 100644 index 96b1389..0000000 --- a/postman/collections/RestoReserve Collection/Gets reservations by status.request.yaml +++ /dev/null @@ -1,5 +0,0 @@ -$kind: http-request -name: Gets reservations by status -url: http://localhost:8080/api/v1/reservations/status/CONFIRMED -method: GET -order: 4411268052689732 diff --git a/postman/collections/RestoReserve Collection/Login Admin.request.yaml b/postman/collections/RestoReserve Collection/Login Admin.request.yaml new file mode 100644 index 0000000..8a27a08 --- /dev/null +++ b/postman/collections/RestoReserve Collection/Login Admin.request.yaml @@ -0,0 +1,12 @@ +$kind: http-request +name: Login Admin +url: http://localhost:8080/api/v1/auth/login +method: POST +body: + type: json + content: |- + { + "username": "admin", + "password": "Admin1234" + } +order: 156544645875 diff --git a/postman/collections/RestoReserve Collection/Login user.request.yaml b/postman/collections/RestoReserve Collection/Login user.request.yaml index 35b207f..87114e1 100644 --- a/postman/collections/RestoReserve Collection/Login user.request.yaml +++ b/postman/collections/RestoReserve Collection/Login user.request.yaml @@ -9,4 +9,4 @@ body: "username": "armymoves", "password": "1234" } -order: 4485838375407470 +order: 137079644081 diff --git a/postman/collections/RestoReserve Collection/Register new user.request.yaml b/postman/collections/RestoReserve Collection/Register new user.request.yaml index b14fb3a..84b2773 100644 --- a/postman/collections/RestoReserve Collection/Register new user.request.yaml +++ b/postman/collections/RestoReserve Collection/Register new user.request.yaml @@ -12,4 +12,4 @@ body: "email": "tana@gmail.com", "password": "1234" } -order: 4497869906067502 +order: 66320542482 From 55475828fad66dfc62f19df250e5af7dad99d2be Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 12 May 2026 18:02:43 +0100 Subject: [PATCH 21/47] fix: validate past reservation dates and add Mockito tests --- .../service/ReservationService.java | 4 + .../service/ReservationServiceTest.java | 83 ++++++++++++++++++ .../service/ReservationService.class | Bin 11491 -> 11597 bytes .../service/ReservationServiceTest.class | Bin 0 -> 5088 bytes 4 files changed, 87 insertions(+) create mode 100644 src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java create mode 100644 target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 630bcbf..3fd84be 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -32,6 +32,10 @@ public class ReservationService { public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUsername) { + if (dto.reservationDate().isBefore(LocalDateTime.now())) { + throw new BusinessLogicException("La fecha y hora no pueden ser anteriores a la actual"); + } + AppUser user = appUserRepository.findByUsername(currentUsername) .orElseThrow(() -> new ResourceNotFoundException("Usuario " + currentUsername + " no encontrado")); diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java new file mode 100644 index 0000000..e89e073 --- /dev/null +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java @@ -0,0 +1,83 @@ +package com.pachedev.restoreserve.service; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.pachedev.restoreserve.dto.ReservationRequestDTO; +import com.pachedev.restoreserve.exception.BusinessLogicException; +import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.TableLocation; +import com.pachedev.restoreserve.model.enums.UserRole; +import com.pachedev.restoreserve.repository.AppUserRepository; +import com.pachedev.restoreserve.repository.ReservationRepository; +import com.pachedev.restoreserve.repository.RestaurantTableRepository; + +@ExtendWith(MockitoExtension.class) +public class ReservationServiceTest { + + @Mock + private ReservationRepository reservationRepository; + + @Mock + private RestaurantTableRepository tableRepository; + + @Mock + private AppUserRepository appUserRepository; + + @InjectMocks + private ReservationService reservationService; + + /** + * Comprobar que no se pueda realizar una reserva con una hora o fecha anterior + * a la actual + */ + @Test + void createPastReservation() { + + LocalDateTime pastTime = LocalDateTime.now().minusHours(1); + ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4); + + assertThrows(BusinessLogicException.class, () -> reservationService.create(dto, "tana")); + } + + /** + * Comprobar que no se pueda realizar una reserva con una cantidad de comensales + * superior a la que tiene la mesa + */ + @Test + void createReservationExceedingPax() { + + LocalDateTime date = LocalDateTime.now().plusDays(1); + ReservationRequestDTO dto = new ReservationRequestDTO(1L, date, 20); + + AppUser user = new AppUser(); + user.setId(1L); + user.setName("Tanausú Febles"); + user.setTelephone("600000000"); + user.setEmail("ertana@gmail.com"); + user.setUsername("tana"); + user.setPassword("$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi"); + user.setRole(UserRole.ROLE_USER); + user.setActive(true); + + RestaurantTable table = new RestaurantTable(); + table.setId(1L); + table.setName("salon 001"); + table.setMaxPax(4); + table.setLocation(TableLocation.SALON); + when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); + when(tableRepository.findById(1L)).thenReturn(Optional.of(table)); + + assertThrows(BusinessLogicException.class, () -> reservationService.create(dto, "tana")); + } +} diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index 88eef1e2da70af0b3fe0c53a5708c2c8c55531f5..6e63904c18b098efaddaae452fe534ea4d06f9c2 100644 GIT binary patch delta 4429 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 diff --git a/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class b/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class new file mode 100644 index 0000000000000000000000000000000000000000..43f070d31959d57215118000ea8918e56c73c794 GIT binary patch literal 5088 zcmbVP340UA8GgqIR+hJcC_ri;DWN8@0V8vUVsjW{z(mGI5_TbN!&(~KYouMF-SyFy zCh2|OSK72q@APhHDh#w~lk`l}`@XOL(WmdMBx`LW)y2=_UG2<#-@NmF=REhHYfk|< zfPZUf5ZITtbFsXU9+w$887oM~wfT^RNf}d5Gc98Y|J!g)+e(&(Qe37XB(Q42m^5Np z!y1bXj7-S1E3l>{?k`WsyzQ9OI3v&%uf0=YZe4J*s&uD7bHcM+Gbe{k#~jJZZp*S= ze-B3>8n+8$v7DX0z`bJqd{bbhYj9mvhrr<_BXA9`U|4Qyz6L72)yU`1ke8s^eM?i@ zy-| zu2ZSnq~pbC5s2hW%X3cHUV*3D(0a1Al!m(ncGY&_3_UM!oP@mS@wi7*0}-@hi-tCV zTdKhlx2-W9Td_?*n>4bX92gb2ZEk-*pwt?I-Hr|oF}hw=y3caum@Fs-I|SCY_LX7# zV&zFnZpJQU-Q5C*T1#wH_10u3u&|=3ZAimj8ol=6<#bx+Rep#a@fMm zjxyow)A17Q=XvF{Ona^rc(nEA-KdUaO-ppP)$p)U&r18~dwpoi-$`E=4r(|cuu&zK z#ctW{Fc_7TqvH?`3+RSFk{U1AQ}m`}kn^4_Cp-U-?SmHeHTs|g!g+>`a(5N(wLF|r zKoSJ@+!RX!ca$PD;LggDGGkg}gT}OqpW`}CsQ3xzvz~L*m~k|m6xd$dLvqYF*_dPs zcV}YdR2V@YR;x$9j#C&ASnf!-FQWn>;Dvq|v<-!EnprAkScd03du`(}Nh3K*IH@Cr zGxRG8pE49NYl7lQw?NpPO2M!|$(`_3e%#vF6nRGPtavHKrV%^I*F~4ltS>{O#^o_>EK)m;XGs)hBKt~-vsTeM(g2B;;i#iIR zpDW%I)XVHxFP?9xr)?+#4^tW@=jFE}HuV{n%5T#uznu$4LwVB2@Jb!8!mD{0V(4ZF zPZGu2p!F+@RSJ6zOY0IHl%5}K0V24h9Q6|V!+5RQ&B)r;#*U61YAmT9Z_x2Zyr~4F z-Ld}DCrL=xD_F`3U)IrxNE5z_uWR_4z`@!+AF#p#kFWN(I-XDm374$V zmx*$VeWWkLwu>*1iEe2LB$)Zx1ul10MWe1IXN}xQ#%SsIAAVb>Az&*U znd?$m#J)wJsrFfOg;(+Vl#UnRmI%I!XEZ!5u)8*|wYzj3&*FRR#!CfO;K6$FRvUMq zJkA9|Z8(ecIC*w0lD%nwL^hIUjBJZ37ptPfQGWcAj!U9lsutwt*&r(J0|suAo} zzcRz7JFblF_qY|YN@UINsnkt{;pxjM4HR3&^zAxs!N~VZcihgf-v1Oo*YGoeO;szI z)}(!bmjT}yxX>6i((1~o;}`fP6I`x3B=CGN-pi6DFJx^#&*+(j_U*zSkWfaO@hFO6U79p<}1 z<)%ertQw2P7;AyCm6To1ZytnCb)AUuE!60iE4b|mU-cS3)keTNtmRWFRbsc}g?t0e z6mFP{iT3DF5if0z-e1Ja+oR`;7-^5nBF5S;W1@(BduSGJ5i{3u ziNX)=4>gBM;tw^4q7N7Gy5^8NYZ0$+4qe8Zi+JajC-L6s`Kx$;0}uU?_E|i36`yOs zFvq{7M6cp2{!BA9m#0cBU0%pi0;_qVF0yohW;ujSIE*c1q=TI7B_Ca6@_vPu(=_wB8UzwcDdT=J-$0gq}bZAxVfubWYJ zda*L1`2i6$;D^40D&PMR|9^ZxL>kZGC!AZ$*Tmc5 I{)OiM0tQIuzyJUM literal 0 HcmV?d00001 From 6d641931d67bf79df1b9876495f7fc1e46c3b0a7 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 12 May 2026 19:48:56 +0100 Subject: [PATCH 22/47] docs: add swagger configuration --- .../restoreserve/config/SwaggerConfig.java | 31 ++++++++++++++++++ .../restoreserve/security/SecurityConfig.java | 7 ++-- src/main/resources/data.sql | 2 ++ .../restoreserve/config/SwaggerConfig.class | Bin 0 -> 2293 bytes .../security/SecurityConfig.class | Bin 6237 -> 6331 bytes target/classes/data.sql | 2 ++ 6 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java create mode 100644 target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class diff --git a/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java b/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java new file mode 100644 index 0000000..ce3f00d --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java @@ -0,0 +1,31 @@ +package com.pachedev.restoreserve.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI customOpenAPI() { + + String securitySchemeName = "RestoReserveToken"; + + return new OpenAPI().info(new Info() + .title("RestoReserve API") + .version("1.0") + .description("REST API for managing restaurant tables and reservations")) + .addSecurityItem(new SecurityRequirement().addList(securitySchemeName)) + .components(new Components().addSecuritySchemes(securitySchemeName, new SecurityScheme() + .name(securitySchemeName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT"))); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java b/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java index d947794..e362532 100644 --- a/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java +++ b/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java @@ -16,7 +16,7 @@ * Configuración de seguridad de la API: * - Define cómo se protegen los datos (hash). * - Configura la sesión como stateless mediante JWT. - * - Declara qué usuarios pueden acceder los endpoints. * + * - Declara qué usuarios pueden acceder los endpoints. * */ @Configuration @EnableWebSecurity @@ -58,7 +58,10 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests( - auth -> auth.requestMatchers("/api/v1/auth/login", "/api/v1/auth/register").permitAll() + auth -> auth + .requestMatchers("/api/v1/auth/login", "/api/v1/auth/register", "/swagger-ui.html", + "/swagger-ui/**", "/v3/api-docs/**") + .permitAll() .requestMatchers("/api/v1/tables/**").hasRole("ADMIN") .requestMatchers("/api/v1/reservations/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated()) diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index e037407..b3cfb26 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -10,10 +10,12 @@ VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); + -- test credentials-> username: armymoves - password: 1234 INSERT INTO users (name, telephone, email, user_name, password, role, active) VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true ); +-- test credentials-> username: admin - password: Admin1234 INSERT INTO users (name, telephone, email, user_name, password, role, active) VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true ); diff --git a/target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class b/target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class new file mode 100644 index 0000000000000000000000000000000000000000..3eb66f5173a35a1a5f2afb25c41120d12ed70bc5 GIT binary patch literal 2293 zcmbtVX;T|V5Pid?!%J)wjE%7q*^mTG%t9O=Sh54gCI~A)kb)0#4bmX5VP}=yl^p-8 zBp=pQp(7^>G zy6vkV^f)NLuI!Shx1EYzcr7axu4u@AkaNN zu_4g2;FVPp-AGvIMW4Xgyrb1h&D~OdQEu%jfmGfr$=waOY|j%No=@OJ&C?a9D)32Qg7Tf6w#@On*$j;l7Dz#)uzt-Vl& zj;D#@M&9!)c2M;lUD@`ft6qElj!`94zlOFv*lIsiQfK1$P+(x)2*|+*SoC(3j^VsO z`oEAQF^EJA7X_}h-RkIV&(3j3;sO$Jj4%*Kk4uxcFnV>21$XyHd*vy{DN#p{)5* zhw0D+H%LoeHgBm)Q(WV5+`@$Mc&4m^lJ8WF>PCl23!mc#bI2eOSQ>91$3v_g2<-VG>hFnBfZxH}NG^kmYjT37Gg#PN#0CQ!?6sA$`#n`AVf#EC(`Dgz-U`-7Cg7=X$lb}F>4a_KJRGT-N(QbSY2F} zgRBuTi#ZGPc*p>2-YJ2_|EXZpdnSQLSd8KEsbbvxcC7fOsw{ksC4yQi7S{ysv`@M= z^cqeW!Yr08JVBnWLb?^$IZfP-0P1u}+hE|8U12TWQqotxfwE@dTO)N}&Wf8 zr$uA?&ccSVSp6^O#`YfJVc#apW&_`0RiHk*%Y>rivQ)|_U+F`CW?!)&Me!!0c zNp^%*4hrNtNP6V^6C^%a?rYhkxYEeU)Nzu8Q1EJgNi8~!_Vur}IAt^uxP8)&(QKwH zk*4Ue4Ouy@*Qr8O{gtO-FSwd@H44(LpILX TI-pnLy3Bu))<;ZqH8=bo2*8%q literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/security/SecurityConfig.class b/target/classes/com/pachedev/restoreserve/security/SecurityConfig.class index e148e2d88c133f13e3371bd1ede84a20774ea91f..060d84328d114f0ae10bb43d17db4a2ca1dea607 100644 GIT binary patch delta 688 zcmY+C+fNfg6vn@ymt8kwyKJ#W9)J*yNn>3bUwjZ03NdJ?35^epNwLaWs=dH&TLhJg z_j=b6?}GQcC{$~rf%w3a|AUF~#V7v-<7w%GW^(47`Q)10NU_r zR$1iRWyBd&-5QC-7S_xWTTWN_>Gje2Y-;UCECvj{3kAtNEp zP!s`I(ZMl6ZnqecgyKSVNwSF?M;;@h*)_#R#e&Q0In3bb=<4kWDKK%AV;lwX z%Qd4?#+lqS_}bUKiCcN+tax7z5<2^9GwWdotRb; zR8eJ5IeHNy_C!Uf#$Itm(NFB9ihc&(lTI6EFd4Hf!;xk4*DuF_nGYgk5Ep-ND`PBBT*LU#8krtlDt ns?}pW!}DtO0gnN1D1-e4=!l>g delta 646 zcmYk4%TH556o^d;(UIE;JezuH2cpCGjsYPD_m}&Y3gc{C;O9GxMcxZln3*&*yJIJ=v`JT`o51 zBkJaWU{sAbs?@BzR!w_$xg-baR})Jbj6;Ihf~EDU!{aWEk&q-AP~D#KfGsEwMv~Fs zP+c$`>5W--Fc2tZP~d9Z-rf>2iE&tRgkiO|Y$|w6u>7AqW!YA0D3XpQ;`62x0%Lc0 zM`zxXk(^>gIlZ51`Zyyw%VvQYi4S*L1A~^G&TGy~E-*0j%e&K2auem|mRjXs=e*4w z)$0E(Zm10vl^yp?1P>)mG?$g|z)Q220(&B9C#2c9mr#+tlyuXh*$Xc{0@)IeTdC$) z#I`Nl&0E15_0wPFu~)Yx68(coH}3`2d8Fnlq%kGvUT|AAGp$R@g{zTTnr0%)wusC) zE`1o-6L5WHZ@%yyF8a^#7V username: armymoves - password: 1234 INSERT INTO users (name, telephone, email, user_name, password, role, active) VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true ); +-- test credentials-> username: admin - password: Admin1234 INSERT INTO users (name, telephone, email, user_name, password, role, active) VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true ); From 9ef9efd5fc69ebc145962f893b4e01fee3fadab6 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 12 May 2026 20:40:06 +0100 Subject: [PATCH 23/47] docs: add javadocs to controllers and services --- .../restoreserve/config/SwaggerConfig.java | 8 +++++ .../controller/AuthController.java | 10 ++++++ .../controller/ReservationController.java | 21 +++++++++++ .../controller/RestaurantTableController.java | 34 +++++++++++++++++- .../service/ReservationService.java | 33 +++++++++++++++++ .../service/RestaurantTableService.java | 25 +++++++++++++ .../restoreserve/config/SwaggerConfig.class | Bin 2293 -> 2293 bytes .../controller/AuthController.class | Bin 2618 -> 2618 bytes .../controller/ReservationController.class | Bin 4687 -> 4687 bytes .../RestaurantTableController.class | Bin 6737 -> 6737 bytes .../service/ReservationService.class | Bin 11597 -> 11597 bytes .../service/RestaurantTableService.class | Bin 7937 -> 7937 bytes 12 files changed, 130 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java b/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java index ce3f00d..eb37de4 100644 --- a/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java +++ b/src/main/java/com/pachedev/restoreserve/config/SwaggerConfig.java @@ -9,9 +9,17 @@ import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; +/** + * Configuración de OpenAPI/Swagger para documentar la API y definir el esquema + * JWT. + */ @Configuration public class SwaggerConfig { + /** + * Define la información básica de la API y el esquema de seguridad bearer para + * Swagger. + */ @Bean public OpenAPI customOpenAPI() { diff --git a/src/main/java/com/pachedev/restoreserve/controller/AuthController.java b/src/main/java/com/pachedev/restoreserve/controller/AuthController.java index 696678a..45ccae1 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/AuthController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/AuthController.java @@ -16,6 +16,9 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +/** + * Controlador encargado de la autenticación y el registro de usuarios. + */ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/auth") @@ -23,11 +26,18 @@ public class AuthController { private final AuthService authService; + /** + * Autentica a un usuario y devuelve un token JWT si las credenciales son + * válidas. + */ @PostMapping("/login") public ResponseEntity login(@Valid @RequestBody AuthRequestDTO dto) { return ResponseEntity.ok(authService.login(dto)); } + /** + * Registra un nuevo usuario cliente en el sistema. + */ @PostMapping("/register") public ResponseEntity register(@Valid @RequestBody RegisterUserRequestDTO dto) { return ResponseEntity.status(HttpStatus.CREATED).body(authService.register(dto)); diff --git a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java index 90bec25..fe56477 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/ReservationController.java @@ -21,6 +21,10 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +/** + * Controlador encargado de gestionar las operaciones relacionadas con las + * reservas. + */ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/reservations") @@ -28,6 +32,9 @@ public class ReservationController { private final ReservationService reservationService; + /** + * Crea una nueva reserva asociándola al usuario autenticado. + */ @PostMapping public ResponseEntity create(@Valid @RequestBody ReservationRequestDTO dto) { @@ -39,21 +46,35 @@ public ResponseEntity create(@Valid @RequestBody Reserva return ResponseEntity.status(HttpStatus.CREATED).body(response); } + /** + * Devuelve las reservas visibles para el usuario autenticado según su rol. + */ @GetMapping public ResponseEntity> findAll() { return ResponseEntity.ok(reservationService.findAll()); } + /** + * Busca una reserva por su identificador si el usuario tiene permisos para + * verla. + */ @GetMapping("/{id}") public ResponseEntity findById(@PathVariable Long id) { return ResponseEntity.ok(reservationService.findById(id)); } + /** + * Devuelve las reservas filtradas por estado según el rol del usuario + * autenticado. + */ @GetMapping("/status/{status}") public ResponseEntity> findByStatus(@PathVariable ReservationStatus status) { return ResponseEntity.ok(reservationService.findByStatus(status)); } + /** + * Cancela una reserva existente cambiando su estado a CANCELLED. + */ @DeleteMapping("/{id}") public ResponseEntity cancelReservation(@PathVariable Long id) { reservationService.cancelReservation(id); diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java index 0532ab9..72b5168 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -23,6 +23,10 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +/** + * Controlador encargado de gestionar las operaciones relacionadas con las + * mesas. + */ @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/tables") @@ -30,6 +34,9 @@ public class RestaurantTableController { private final RestaurantTableService restaurantTableService; + /** + * Crea una nueva mesa en el sistema. + */ @PostMapping public ResponseEntity create(@Valid @RequestBody RestaurantTableRequestDTO dto) { RestaurantTableResponseDTO response = restaurantTableService.create(dto); @@ -37,16 +44,25 @@ public ResponseEntity create(@Valid @RequestBody Res return ResponseEntity.status(HttpStatus.CREATED).body(response); } + /** + * Busca una mesa por su identificador. + */ @GetMapping("/{id}") public ResponseEntity findById(@PathVariable Long id) { return ResponseEntity.ok(restaurantTableService.findById(id)); } + /** + * Devuelve la lista completa de mesas que hay registradas. + */ @GetMapping public ResponseEntity> findAll() { return ResponseEntity.ok(restaurantTableService.findAll()); } + /** + * Actualiza los datos de una mesa existente. + */ @PutMapping("/{id}") public ResponseEntity update(@PathVariable Long id, @Valid @RequestBody RestaurantTableRequestDTO dto) { @@ -55,28 +71,44 @@ public ResponseEntity update(@PathVariable Long id, return ResponseEntity.ok(response); } + /** + * Elimina una mesa por su id. + */ @DeleteMapping("/{id}") public ResponseEntity delete(@PathVariable Long id) { restaurantTableService.delete(id); return ResponseEntity.noContent().build(); } + /** + * Devuelve las mesas cuya capacidad es suficiente para el número de comensales + * indicado. + */ @GetMapping("/capacity/{pax}") public ResponseEntity> findByMaxPax(@PathVariable Integer pax) { return ResponseEntity.ok(restaurantTableService.findByMaxPax(pax)); } + /** + * Devuelve las mesas filtradas por estado. + */ @GetMapping("/status/{status}") public ResponseEntity> findByStatus(@PathVariable TableStatus status) { return ResponseEntity.ok(restaurantTableService.findByStatus(status)); } + /** + * Devuelve las mesas filtradas por ubicación. + */ @GetMapping("/location/{location}") public ResponseEntity> findByLocation(@PathVariable TableLocation location) { return ResponseEntity.ok(restaurantTableService.findByLocation(location)); } - // '/location-status?location=SALON&status=AVAILABLE' + /** + * Devuelve las mesas filtradas por ubicación y estado. + * '/location-status?location=SALON&status=AVAILABLE' + */ @GetMapping("/location-status") public ResponseEntity> findByLocationAndStatus( @RequestParam TableLocation location, @RequestParam TableStatus status) { diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 3fd84be..172e95a 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -30,6 +30,13 @@ public class ReservationService { private final RestaurantTableRepository tableRepository; private final AppUserRepository appUserRepository; + /** + * Crea una reserva validando: + * - que la fecha sea en el futuro + * - capacidad de la mesa + * - que una mesa previamente reservada estará ocupada por dos horas, por lo + * que la hora de reserva solicitada no entrará en conflicto. + */ public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUsername) { if (dto.reservationDate().isBefore(LocalDateTime.now())) { @@ -74,6 +81,10 @@ private ReservationResponseDTO toResponseDTO(Reservation reservation) { } + /** + * Comprueba si ya existe una reserva confirmada en la misma mesa dentro de la + * franja horaria de 2 horas. + */ private Reservation findConflictingReservation(Long tableId, LocalDateTime requestedReservationTime) { List confirmedReservations = reservationRepository.findByTableIdAndStatus(tableId, ReservationStatus.CONFIRMED); @@ -123,6 +134,10 @@ private List toResponseList(List reservatio return response; } + /** + * Devuelve una reserva por su id si el usuario autenticado tiene permisos para + * verla. + */ public ReservationResponseDTO findById(Long id) { Reservation reservation = getReservation(id); @@ -133,6 +148,10 @@ public ReservationResponseDTO findById(Long id) { return toResponseDTO(reservation); } + /** + * Devuelve las reservas con el estado indicado aplicando restricciones según el + * rol del usuario autenticado. + */ public List findByStatus(ReservationStatus status) { AppUser user = getUser(); @@ -147,6 +166,10 @@ public List findByStatus(ReservationStatus status) { return toResponseList(reservations); } + /** + * Cancela una reserva si el usuario autenticado tiene permisos para + * modificarla. + */ public void cancelReservation(Long id) { Reservation reservation = getReservation(id); @@ -164,12 +187,19 @@ public void cancelReservation(Long id) { } + /** + * Verifica que el usuario autenticado pueda acceder o modificar la reserva + * indicada. + */ private void validateReservationAccess(Reservation reservation, AppUser user) { if (!user.getRole().name().equals("ROLE_ADMIN") && !reservation.getUser().getId().equals(user.getId())) { throw new BusinessLogicException("Su usuario no tiene permisos para acceder a la reserva especificada"); } } + /** + * Obtiene el usuario autenticado actual a partir del contexto de seguridad. + */ private AppUser getUser() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String username = authentication.getName(); @@ -179,6 +209,9 @@ private AppUser getUser() { return user; } + /** + * Busca una reserva por su identificador o lanza una excepción si no existe. + */ private Reservation getReservation(Long id) { Reservation reservation = reservationRepository.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java index d1850a2..9af649e 100644 --- a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -21,6 +21,9 @@ public class RestaurantTableService { private final RestaurantTableRepository restaurantTableRepository; + /** + * Crea una nueva mesa con estado inicial AVAILABLE. + */ public RestaurantTableResponseDTO create(RestaurantTableRequestDTO dto) { RestaurantTable table = new RestaurantTable(); @@ -35,6 +38,9 @@ public RestaurantTableResponseDTO create(RestaurantTableRequestDTO dto) { return toResponseDTO(savedTable); } + /** + * Busca una mesa por su identificador. + */ public RestaurantTableResponseDTO findById(Long id) { RestaurantTable table = getTableById(id); @@ -47,6 +53,9 @@ private RestaurantTable getTableById(Long id) { return table; } + /** + * Devuelve la lista completa de mesas registradas. + */ public List findAll() { List tables = restaurantTableRepository.findAll(); List response = toResponseList(tables); @@ -71,6 +80,9 @@ public RestaurantTableResponseDTO toResponseDTO(RestaurantTable table) { table.getLocation()); } + /** + * Actualiza los datos principales de una mesa existente. + */ public RestaurantTableResponseDTO update(Long id, RestaurantTableRequestDTO dto) { RestaurantTable table = getTableById(id); @@ -87,6 +99,10 @@ public void delete(Long id) { restaurantTableRepository.delete(getTableById(id)); } + /** + * Devuelve las mesas cuya capacidad es mayor o igual al número de comensales + * indicado. + */ public List findByMaxPax(Integer pax) { List tables = restaurantTableRepository.findByMaxPaxGreaterThanEqual(pax); List response = toResponseList(tables); @@ -94,6 +110,9 @@ public List findByMaxPax(Integer pax) { return response; } + /** + * Devuelve las mesas filtrando por estado. + */ public List findByStatus(TableStatus status) { List tables = restaurantTableRepository.findByStatus(status); List response = toResponseList(tables); @@ -101,6 +120,9 @@ public List findByStatus(TableStatus status) { return response; } + /** + * Devuelve las mesas filtrando por ubicación. + */ public List findByLocation(TableLocation location) { List tables = restaurantTableRepository.findByLocation(location); List response = toResponseList(tables); @@ -108,6 +130,9 @@ public List findByLocation(TableLocation location) { return response; } + /** + * Devuelve las mesas filtrando por ubicación y estado. + */ public List findByLocationAndStatus(TableLocation location, TableStatus status) { List tables = restaurantTableRepository.findByLocationAndStatus(location, status); List response = toResponseList(tables); diff --git a/target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class b/target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class index 3eb66f5173a35a1a5f2afb25c41120d12ed70bc5..abc9adf41f30ebd80111f19c722920a0f7e43632 100644 GIT binary patch delta 71 zcmew=_*HPjeRf8{$q(4)nMyG*GsrLqGRQKBF~~7UGsrV20Qt&5(vU%s!GS@E!3D_k WVo+i5XHaDb0g6R2$V}eM;RFErwF}Mw delta 71 zcmew=_*HPjeRf9P$q(4)nF=v5Gl(zU$^Qk|CW{HAGulrQ6rVg*AdAssl3*Ka00Rd@ P(By8xBu3fE?*tP8^289U delta 60 zcmX@Fa$aRaB|oP!10RDbg9L;6h-Nd|Om+~fWOZfWVsM|lPi!r#KLZCtz~ln) jM#jj=pMYfI~mzBsv(2C)Z0BFv?HEN4AB6O?4{2yZvN%WFKA7*-#qrg|{ zBpMfSI7wt!@<%0iOp?ZFW>PMv=x3S%;m4T0HWkKd8S`?oAaxhXmLf$gV`W8(ttz|O zm$D&dZi(0w)-AQS)SuaJ+$DxR64)mNj|vo>XB>!&S6mLo

MMGXF$oovAfTF#A&U ze$iiv{)Q}W(YdFJ2W$k?NU_%tl#hB8_DMxqcT!(45F(jZG6~DLSLyYpPOWps590YG gjSm_3rE*ImTDeNp?OLVk(;Kv~+R<&=r+U`i|0>p6xBvhE delta 394 zcmWNNNhri&7{-79vTG~{gvc_+nE6xIWNmU7E@RJ@rW^ql>$PUaZBlHX=x_EyxD92JWN%V diff --git a/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class b/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class index e2ed22c5ae746fb13666d3217842d0c27a4d818d..417a1c0a7ce9b24d7eeed6e3e0d8b6a5ba2267e7 100644 GIT binary patch delta 213 zcmZp)YqZ-CFD@y|z`-ETAi$u&AieoWXQ*szd>&4N$}usMCJ(J&C1)&Omcq8RQt;7_@-234_PvDUw?`{TW0U z0)Qs_PY#l55Df>K7RkWJ5Ct?R8mKmwL6;$Z@^h(L)?}b)%H&Mxbk1xBZiXBNL594^ zH>Ia@mI8TYKwibR>Qg#++Cj$>d7lSZE&*VL_{}@ds@0N1|01s>{ AZ~y=R delta 213 zcmWNJI}3qv6viI|`MX6f?}e9S5QV{IkjwTOgfLJOnecGRlbCr z&hYemdd}}yI+kujNl{@&08VI-5k!bDiZHT>&{kR)qc9;xUukJiz=9-vNI*i0Ch|(8 zxykTnj^6Usm1j?Z4MoH`GbqzZxeFa0RM@OjOFek2Y^%Y6I(r(tZL+ORA9Zi+aNDJs ue=2&gq7N4a^zxs@h-u6;p Date: Tue, 12 May 2026 23:09:07 +0100 Subject: [PATCH 24/47] docs: add missing javadoc to auth service register --- .../restoreserve/service/AuthService.java | 9 ++++++++- .../restoreserve/service/AuthService.class | Bin 4943 -> 4943 bytes 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/pachedev/restoreserve/service/AuthService.java b/src/main/java/com/pachedev/restoreserve/service/AuthService.java index 8b559b9..2cc51e5 100644 --- a/src/main/java/com/pachedev/restoreserve/service/AuthService.java +++ b/src/main/java/com/pachedev/restoreserve/service/AuthService.java @@ -17,7 +17,7 @@ /** * Servicio encargado de autenticar usuarios mediante JWT. - * Comprueba que las credenciales introducidad sean correctas y si el login es + * Comprueba que las credenciales introducidas sean correctas y si el login es * válido genera un token para acceder a los endpoints protegidos de la API. */ @Service @@ -48,6 +48,13 @@ public AuthResponseDTO login(AuthRequestDTO dto) { return new AuthResponseDTO(token); } + /** + * Registra un nuevo usuario cliente validando que el nombre de usuario y el + * email no estén ya en uso. + * + * @param dto datos necesarios para registrar el nuevo usuario. + * @return mensaje de confirmación del registro realizado. + */ public String register(RegisterUserRequestDTO dto) { if (appUserRepository.existsByUsername(dto.username())) { diff --git a/target/classes/com/pachedev/restoreserve/service/AuthService.class b/target/classes/com/pachedev/restoreserve/service/AuthService.class index 343a19ad2202ba399a8d54b01eae279c036691b7..d3381c2d6207e39eb9d6f40fdb289bd11596b79e 100644 GIT binary patch delta 74 zcmX@Fc3y46Q9)Z91_1_J1`!5323a7j$l%Ce$lwHot_)rbZVW*T?hH{39t=qgo(x$) Zk!l8Sh9(9dhAsv_hN%nzo0)`s837$^4bK1o delta 74 zcmX@Fc3y46Q9)Z%1_1^$1`!5x23a7j$Y8}_$Y9Oj#9+tZ#bD16#NfaX#o)-01Qf|) baAv4xaA9a-aAoLXaA%mx;JKMe$d?fS4Xh1c From 4b43e0426cd0d8bde4d11b8aeb4181bb57ed42a3 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 14 May 2026 20:21:57 +0100 Subject: [PATCH 25/47] feat: add penalization points and status to AppUser --- .../restoreserve/model/entity/AppUser.java | 7 +++++++ .../restoreserve/model/enums/UserStatus.java | 6 ++++++ .../restoreserve/service/AuthService.java | 3 +++ src/main/resources/data.sql | 16 ++++++---------- .../restoreserve/model/entity/AppUser.class | Bin 3230 -> 4030 bytes .../restoreserve/model/enums/UserStatus.class | Bin 0 -> 1217 bytes .../restoreserve/service/AuthService.class | Bin 4943 -> 5287 bytes target/classes/data.sql | 16 ++++++---------- 8 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/model/enums/UserStatus.java create mode 100644 target/classes/com/pachedev/restoreserve/model/enums/UserStatus.class diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java b/src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java index d475a04..08c9e75 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/AppUser.java @@ -1,6 +1,7 @@ package com.pachedev.restoreserve.model.entity; import com.pachedev.restoreserve.model.enums.UserRole; +import com.pachedev.restoreserve.model.enums.UserStatus; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -47,4 +48,10 @@ public class AppUser { @Column(name = "active", nullable = false) private Boolean active = true; + @Column(name = "penalization_points", nullable = false) + private Integer penalizationPoints; + + @Enumerated(EnumType.STRING) + @Column(name = "user_status", nullable = false) + private UserStatus status; } diff --git a/src/main/java/com/pachedev/restoreserve/model/enums/UserStatus.java b/src/main/java/com/pachedev/restoreserve/model/enums/UserStatus.java new file mode 100644 index 0000000..431208f --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/model/enums/UserStatus.java @@ -0,0 +1,6 @@ +package com.pachedev.restoreserve.model.enums; + +public enum UserStatus { + ACTIVE, + BANNED +} diff --git a/src/main/java/com/pachedev/restoreserve/service/AuthService.java b/src/main/java/com/pachedev/restoreserve/service/AuthService.java index 2cc51e5..600249f 100644 --- a/src/main/java/com/pachedev/restoreserve/service/AuthService.java +++ b/src/main/java/com/pachedev/restoreserve/service/AuthService.java @@ -10,6 +10,7 @@ import com.pachedev.restoreserve.exception.ResourceNotFoundException; import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.enums.UserRole; +import com.pachedev.restoreserve.model.enums.UserStatus; import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.security.JwtService; @@ -74,6 +75,8 @@ public String register(RegisterUserRequestDTO dto) { user.setPassword(passwordEncoder.encode(dto.password())); user.setRole(UserRole.ROLE_USER); user.setActive(true); + user.setPenalizationPoints(0); + user.setStatus(UserStatus.ACTIVE); appUserRepository.save(user); diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index b3cfb26..d657bae 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -11,19 +11,15 @@ INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); -- test credentials-> username: armymoves - password: 1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true -); +INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true, 0, 'ACTIVE'); -- test credentials-> username: admin - password: Admin1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true -); +INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true, 0, 'ACTIVE'); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED' -); +VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED'); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED' -); \ No newline at end of file +VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED'); \ No newline at end of file diff --git a/target/classes/com/pachedev/restoreserve/model/entity/AppUser.class b/target/classes/com/pachedev/restoreserve/model/entity/AppUser.class index 512da080eef4520f18e864e60cbef5c3e828a155..f818dc31bb3c2230c2da4d435cb5557273ae4b96 100644 GIT binary patch literal 4030 zcmb7`ZC4vb6vyv|gvX^&AXp04TG0loY^AMLTG~=dF*XGxX|(u&OEOIsHoNIQ#Dkwo z&#}iIU-$w1P#*txcNP|i?(!nZ&dh&)bN4 zN4TDiFX42A*0Sru)P&_3-l4YAZoj~EA7vF9-Pb$1X6jZ$+uYk1HBX_8QOA7gc%ful zje81BZTr~Y5>>-B_Dr#2S+=KphHbeDU4r~TcRXEd3&%BFPgpgf736L#Rd%3(X$QGBwtQ7KklDwMVKmQZLcW^~zejKJ)fuII1Xrr)yo zT+2629q-cvPm)5trmz}blShjR4S2#7?WS!7%M%LaL`yeJ$b8Fq?FX|8AU^yV+AzS6 zyWw+xTX)@`Z3q6$I<^T5Z@-T!)>g(3Ss`N|*JP zE8E45b(pW$Jfnjx<1v>#wsE4)P8n+pOE-;Qg79wHhUFo~acR*)sDuOQcwD;MmfBty zso`VO;yGTp9P9yeDDYu~$TfslgbT+Cb1`FOQclaHELxo|X*pIh?be=spdF7gBDl}3uEV1FY_JmX6OyESaYr0w09fQYmGV3)ByiuAE#>)&R_TddSf()HF z@eVRa*_O{$8m0>hsgPEpoa2RiDoyZ0KNi+F_jvMwN|QV}gh@^nPB)%kQE4ig-;%k< z^G{To=6N=R(+Y8cA?Hj*27Rg0484oG0BA*~Q2xX_MM=c!l1kU;1BLQXZ*|G2lRKlF z;+D9T@3ER zhA?jV_!b#9-lRft6fSOJGDrvw9;48NqbMLDHw?qA!g-^>rw)J}d4Do)S>${cJM1zE-5MDp_R1 zCqWO}e5KH2VJBYb(K{(LShjtqCY~Ccz^c5(78s~d_b$H{TzII!>8_nIC~U0exZk++ zK5~Wo{vc(zF<{h((M62<@ynSsC}@aOOrTcfavr}U*f#89a&!msu_BE_f~q|$nK+W< zAwdnFl}v_`cXsF!Bq(Poxf~pn<%2HAB=R(rx73XWI+*C9<^zK=T(fd@8q6zreoxR< zyzmHy5Y9wGljD&Pw=~dkA`Ojzq$F} zN&6KK8Tu68EFrja5#MSkpj&hsBZcnJX9=LY=|Ep3f|k;O?k9ql(}5l)f*z#WJ+&S&4!{u_$<=mIPv+~HZ=Qv84X_|eSPIs*Rt;2{zO+h9E2Sw$(}0jhi;p@a*JRpx#CgQUztXkr z(&Yz#fIrIBeP(W83UST!1DSiz-Dlr(&)Mhj?|*;)LqzM;9HE?`)uwCfU8C8NE!opO z=?5-;q}P+W?Y5+)OD8acLw%#$eFF0cF!ZkUeA5r4)0BFp1-B~$kG7;EJtL5< zhGB&gu%aLEzI~YDf&1!*-N>Wzuw1KG>Q4pb9mAG_rV~N$1)dp&y)^LtP1g!-hwpVl z%QEnuA!L#S4O`M_2OZws5HuP{OLjZ16CKYADoEQfExrVe9$qWt}IzgDjPSB;03iv6tl0r zLCJz+xuMZGofV`3TJM*c4?fmtmJf0|+y8|d!G zGU&Li9mB&b0(30mghQOLxulEA7dvS{9Oa1{R%Hh{!Lpx|c-PNF%p1S|X6a3z&$o_( zyZ`L@WMutUGidm(>6pPiK|`hG#uyFJHyYLH$rwGQ??z}}(A4oXQ+^tKPd^~MsCBk~ z7BpYlPl~-yFKBGf4ZWs(WO5NTH9S|DP|)DvQjX>AtP~fm+`3Z=@QDu*e^U+0lzUX`cqHcr46>c)ZAw(a#j{?fEZq?}`I4@xRj--ot@C3F1F7<(s z>}i_I@#z#!uF!k%cpvHD^n8GC34c1)0^h>QAGG`%7CE|#UxpBJ@gaV-7(gG?P0?eT zA^IKqI<2%DS?S}ksOT{RLBC6TY4qi+^zkrN^cbR`$Nxg;_oVLc!^Wd>eC1DStmOZq sZ(ma4ma~Y6XpZuj2O7S|^K@LbL5_Zmn&nyaAB%_jPuLqE#c)4(1?9OMf&c&j diff --git a/target/classes/com/pachedev/restoreserve/model/enums/UserStatus.class b/target/classes/com/pachedev/restoreserve/model/enums/UserStatus.class new file mode 100644 index 0000000000000000000000000000000000000000..cf9c9d069f95c1bd4cf464ac4c770801021377da GIT binary patch literal 1217 zcmbVL-A~g{6#re?ty?KDr+|F;J#`yM0i!RQ1cZ?+j$u+*viNlAu29mor0tgQ=AWeS z0MST%_Kz~2+fB9?AE3$oIA6c>JLjJM{`36`z&osJ7-3kp{kD0+?IY0=T{942BCs zTMVO>`un^Z^&2^|a*1~Rxq8Owc_seElv zixmZqcUYC2r12RVYv_E3$dVI&H1OI?l5REv)#`O z+rwe~1{QAqZn9Ae2J%>x)efcIba@z-h7{L11~thUq%l%^jnyr`6WC(gk#90FnA{~9 zKs8&=p+}P(kacSaSAvvpPJFGjzCu%>&_AWD&&Y`Z=%ir*GP812$fDAzW0d?1k{Tq- zZ|MhEGdreSVEi1}Sh%Og!ertnnUphRt)_a0N$VWb-zZ`PGqjJ#*;I^CwpqH*#ujO% z*`{6QSRuXSq!kzDFEBRC8V4EiUBkR2i$}E5BY2Pj9@0wVF`o1pzokqPZR!lu z%@0cPs#%rD$r64Y#5_mw{QbjZv7sTL#78OxUiAEoaX$-`aM5+&(*u+O) zOgxFv#C@x~vM=uYF7CVH`Y-sX&TV}#(R*`d=FIoaa^{@=F-gDo-p{^Ezzn`Suqejq zN|o0d=yxVu>|vjSy^5@+$c9MC+DL3oNhlVNu8FQQ*uj3qFki^O@E>rxi!mHDIK*Ma z(3WVtHX4hpZQK}%H#Wy=n;T>C7Bloef&Q?Qqa1UPRHzANC%W0`9bOlY#MieN9Os0> zUO9VNC_Gm&Q6F%PH1UEoo+^9F;526xPAQvqE6VhK*C>-XS@Dzr`H~xAg;Kto1JU^bg~c|E40x_xEuZH#9|8$ESOF;6l-F z(8u{qJCF1)ca`&ri|4#_@Iu#kiZfm*cKURsdxS1`_o z>TQ|Dw$F+po$wUd9eh>fia*L=3Cqetk=10UF5OE7Im~3V=t5>u%50HUg3n_q z^I1WN2H7>SfK3v)g+*+ohV3k77fU!yop_)i-2pWT0n9H+EuAu!YF0_2IxK|6Yr|4l zK)qNQN(cy{oGyv&7TYen9?@=DDmp;#7^87<~X^2^Pc~!9;@57=82)_+Vla-{i%_ z9TQ^|yd&Nbq|z$l9Z)Iq>Wja`xTj{a*E+M-I{VB%Gq$C^*<1Wuodr5sv9S!>q-dHa z{CYGNXAQTS7-3Yp<2%ir$@B3AV;)P<=`rJS*|`{*vB3ZT=u6(Y@Okz z#bE08OO-<$QtZN0>`?3^!(KKhw@{>Aoi6s%M~MM?I6*IGIlwtpE^v@bx_g)-T%(`E z+F{fGqf|MnZ+fWecXi@ERR%bQl+){4?rG@0nh`Y~C_7pwu?QKZ_>e!1SLF#*cp6M$HKqs}qBjwIiKP|hS!4B_7s`PfWGA$8>b=p1 bRo?NQFY0`4;CurY`JtKMPuV}I^NZ|13SVvG diff --git a/target/classes/data.sql b/target/classes/data.sql index b3cfb26..d657bae 100644 --- a/target/classes/data.sql +++ b/target/classes/data.sql @@ -11,19 +11,15 @@ INSERT INTO restaurant_tables (name, max_pax, status, location) VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); -- test credentials-> username: armymoves - password: 1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true -); +INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) +VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true, 0, 'ACTIVE'); -- test credentials-> username: admin - password: Admin1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true -); +INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) +VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true, 0, 'ACTIVE'); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED' -); +VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED'); INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED' -); \ No newline at end of file +VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED'); \ No newline at end of file From 08bc8755457594253ab9ea0cdb622a63a0ae2465 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 14 May 2026 20:57:57 +0100 Subject: [PATCH 26/47] feat: add late cancellation or no-show penalty logic --- .../service/ReservationService.java | 21 +++++++++++++++++- .../service/ReservationService.class | Bin 11597 -> 12114 bytes 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 172e95a..245c667 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -16,6 +16,7 @@ import com.pachedev.restoreserve.model.entity.Reservation; import com.pachedev.restoreserve.model.entity.RestaurantTable; import com.pachedev.restoreserve.model.enums.ReservationStatus; +import com.pachedev.restoreserve.model.enums.UserStatus; import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.repository.ReservationRepository; import com.pachedev.restoreserve.repository.RestaurantTableRepository; @@ -169,6 +170,10 @@ public List findByStatus(ReservationStatus status) { /** * Cancela una reserva si el usuario autenticado tiene permisos para * modificarla. + * + * En caso de cancelación tardía o no-show, le sumamos 2 puntos de penalización + * al cliente. + * Cuando acumule más de 6 puntos, su estado pasará de ACTIVE a BANNED. */ public void cancelReservation(Long id) { Reservation reservation = getReservation(id); @@ -181,10 +186,24 @@ public void cancelReservation(Long id) { throw new BusinessLogicException("La reserva con id " + id + " ya estaba cancelada"); } + LocalDateTime reservationTime = reservation.getReservationDate(); + LocalDateTime limit = reservationTime.minusHours(2); + + AppUser client = reservation.getUser(); + + if (LocalDateTime.now().isAfter(limit)) { + client.setPenalizationPoints(client.getPenalizationPoints() + 2); + + if (client.getPenalizationPoints() > 6) { + client.setStatus(UserStatus.BANNED); + } + + appUserRepository.save(client); + } + reservation.setStatus(ReservationStatus.CANCELLED); reservationRepository.save(reservation); - } /** diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index 54fc7440c494536d5cc18037db676daa8b9012a2..f93c053e9255c9fd447d4741d10564ce3d5c014d 100644 GIT binary patch delta 1826 zcmZ`(X;4#F6#h<@_d*C56g5GXq%MR66i}gns2~VbSrk!0St6#G3Q3y;*IGnv)mrOv zwJxpVR+m;RiUvVzop#z8J6&cv({}vPse_&Vs5+f#f7kZp83%sQnfKnk@80t*=iKGn zx*r;hUq2nV10bJn$WRexh)y|9G#TQq7A-NAjHO%y<)MY3Z*zOv{iWV^pI?t`$~Vw# zloQ6bxB^u!kF(Xi$r*5aJyl+}C%_SvG}nMuDj&>ls_yeWoaaAI7%4hb20xq#rhI=n~R`rb+@?#EYsZTc6kDN{7n%$S|U!G zN~NW=T&88>v8hO1OLbzpxiVrUHON#?$Ps^PADNY(4pUcf@ZaX$QR})0@JOn5^DOyF^yv zgK^u97}SI49Rs~euZ@yu523&&3gmcEmoO>GNbiON4jO13t>=IPJf^4j=%`Fb#GWLF z_88%)O>`!jL{nm!c#gckFv0CVg$h2l^t`a7TuEg=I7p<15w|Bp_ zg`5sVAr<2>4OXNhjoYcnL>{K2gxfOA#0q4=g&g>iiw!)}fmzs%d>q1TT;jGHg$QCE zZlefapcvm^K7M5MFN}VI1!DQc!Kg}%LKPCR2vblgN+xxu)j`Y5I@BQ=^{n5>x=pO+ z#2hrE6sxhARcm;>5$;~=WSPtXhOA?|8<2pFNX91Eu^BI7izt}V9n;BV+gV}=ge_>51A02?r1fffyVXx05fLPcTatvz%s@GraB2!hv&~+6A%2wpLHoP($VeD8yg3 zwCW)0b2Vm7ELH_6Cs&&qM1QVy6R~D(5D$+b%Pg6tAk7M*Lfdqc<^-wm`M*&*he)$@ zjf#V`ztl2Mn1BIZiBGxeceuiLxvHO|oGaRh zFVTvx5Wqcb~XrE^=0xa8f0% zK!aK7SVmRc)w0MUTEx>5t})yM^*>0J(I6xHDN1CFRi5lfaWZALZSiqL-$Fv|7^?1r z^|PxCP;)84E2!r0PrQSE<_zu&jeTiHO+T$5EOhjflTaQiKx#jdRY)D6)!_Y-aTnPY zX+4ZnDI9TZ0r(XWc)+z8L>wOSUK!+N{!Pi$&c1<^aHbKkkc(dCOpP#83(hl3GQZC< zRgqQPx0|P~QNb}l?yy`#dFqhdXoc<(^mvSMc*Gm>7AnsbWxeP Wq87U*2SEZZ&_g%m!JKCAc_zdL;=wR%+y>YepHI0Ux@spP&CPY z$b6~P7*m@`n~4SmMb-u{YPx7}v}T%-W}0d2&Y7BVy3Xm@uWg14x_O^xpZ7WEJm>x1 zcYkWR(d7Q&eta50lp`8UM2R}6>8j1CU##A|)cP0gHkK=kUT2T8Qey>CsqX1zYDl-4 zSFuW+)Elj9SfjC8{i#=&Yq?exdg|@#Fs*St@sKq8RhP%5E_+(7PdHJ?P2+mTAT3Qq zRJD7fwnebA)WO*JUGD&`U3^jF3#vOa*ZdM+RwJ3eEqdOKI}VUNPCmtL3$5%{Ls_-n z-eh^NlTWin%6r`Gb?^=LYkX7P%L?&;^7>9&`fNO;uKQ~EmP+v#Eq~j|HiKnQ+LILT zJGp~9h2lM_+4!Mq@@Hs@@YHwosI&g1W*a|JC;Y{h6GT8&Wi3(eY)!55X;zJ=h-Hb= zxI4R^?Lnj>ge53|4~2+GEI<*;QH-?`*P#U4@d!Hb7fxh8SFE7X22{ zZ55?9RAPr9cVZKEVT;sT(K$YtqcOclAiXl~Zg^#oT<`(&^_bv}1A?SPOCh{PeO z4#?O?R7c)e>JY3rh6Ol|Gz=?${)O}rVL2@@@Wf zOtN`QDkj6Eut{EMOlSh!j>P*(VoQ?P#Rm2Y%CDj`&enMuisvx2_5ZJY`@G;=bHMWU zlJZ4K`7}e`(B3sKeD*&$EbK@)>*h0jR+7X~bpm`&@&-8giX7pqe2uRg*>3LRejb#b o0b@OERFCi|-!ZaxIm8c)onyv&d`>>hkA*YwNAw9#@>8V$1HStG$N&HU From f3e0dd231b5f574b96d6ae8461433f68eeb7b122 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 14 May 2026 22:11:48 +0100 Subject: [PATCH 27/47] feat: prevent banned users from creating reservations --- .../exception/BannedUserException.java | 8 ++++++++ .../exception/GlobalExceptionHandler.java | 8 ++++++++ .../service/ReservationService.java | 12 +++++++++--- .../exception/BannedUserException.class | Bin 0 -> 458 bytes .../exception/GlobalExceptionHandler.class | Bin 4506 -> 5114 bytes .../service/ReservationService.class | Bin 12114 -> 12381 bytes 6 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/exception/BannedUserException.java create mode 100644 target/classes/com/pachedev/restoreserve/exception/BannedUserException.class diff --git a/src/main/java/com/pachedev/restoreserve/exception/BannedUserException.java b/src/main/java/com/pachedev/restoreserve/exception/BannedUserException.java new file mode 100644 index 0000000..776dd6b --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/exception/BannedUserException.java @@ -0,0 +1,8 @@ +package com.pachedev.restoreserve.exception; + +public class BannedUserException extends RuntimeException { + + public BannedUserException(String message) { + super(message); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java index 14ee50a..9154c54 100644 --- a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java @@ -43,4 +43,12 @@ public ResponseEntity handleBusinesLogic(BusinessLogicException e return new ResponseEntity<>(error, HttpStatus.CONFLICT); } + + @ExceptionHandler(BannedUserException.class) + public ResponseEntity handleBannedUser(BannedUserException ex) { + ErrorResponse error = new ErrorResponse("BANNED_USER_ERROR", ex.getMessage(), LocalDateTime.now()); + + return new ResponseEntity<>(error, HttpStatus.UNPROCESSABLE_ENTITY); + + } } \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 245c667..bbbf960 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -10,6 +10,7 @@ import com.pachedev.restoreserve.dto.ReservationRequestDTO; import com.pachedev.restoreserve.dto.ReservationResponseDTO; +import com.pachedev.restoreserve.exception.BannedUserException; import com.pachedev.restoreserve.exception.BusinessLogicException; import com.pachedev.restoreserve.exception.ResourceNotFoundException; import com.pachedev.restoreserve.model.entity.AppUser; @@ -40,13 +41,18 @@ public class ReservationService { */ public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUsername) { + AppUser user = appUserRepository.findByUsername(currentUsername) + .orElseThrow(() -> new ResourceNotFoundException("Usuario " + currentUsername + " no encontrado")); + + if (user.getStatus().equals(UserStatus.BANNED)) { + throw new BannedUserException( + "El usuario se encuentra en estado BANNED debido a múltiples cancelaciones tardías"); + } + if (dto.reservationDate().isBefore(LocalDateTime.now())) { throw new BusinessLogicException("La fecha y hora no pueden ser anteriores a la actual"); } - AppUser user = appUserRepository.findByUsername(currentUsername) - .orElseThrow(() -> new ResourceNotFoundException("Usuario " + currentUsername + " no encontrado")); - RestaurantTable table = tableRepository.findById(dto.tableId()) .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + dto.tableId() + " no encontrada")); diff --git a/target/classes/com/pachedev/restoreserve/exception/BannedUserException.class b/target/classes/com/pachedev/restoreserve/exception/BannedUserException.class new file mode 100644 index 0000000000000000000000000000000000000000..bc5c36c1c2912227a33fe552a86dba90f5364ccd GIT binary patch literal 458 zcmbVIJx{|h5PcWg2122w2!zjn0p+W}`OaUPZ-QDEW8Z~z1xD|hk35kIpz>h*) zpuhkVwsd#z`RU!AU*Dcz0GwbwL_lb#HjgJN9dX99*m2>l1a>oy`61 zgg*b`LR1Lb_iCo%u`W@^07`TLzvJJv?~E@5Lg`O)>f(`MM&>k}$xHsc5jSgNCn z8ljPB!&lRM!0uKJ#!P4>HdW)ka=OHGvf@X&ARHzC9KAybb1n)s{OIs&HbV1~{m5q5 z%Bh?^JLwkn?9`=vq06zh{`io?(g!;rS*ScDSGGccP*7KLAi1`G@Px%jsRBe2qfb_p eA(jPi&T$U;3|I#QVu1fvBjglZZ!h%P8RlNwu0OHCRV^`(6x+LY4QA`Upu zbG=T06HaT;L{t+00C#R&>;K?Njq$V#ShzU%-1EEN`R>WRGwXhJy1xAV^clcTyp~}h zY!{@gFMn%T*e%(-u0wh*lQlwmE@xyCrcoed1W|{wLc}};t$l>)|M?N>Eid@Cu4uK% zw2?24j^+AlVG`V-{%BMiOeNx4ETzR_Loo#)* z9MntMV#F{_eZ;WgA}-6gM6e&$C-gw6IGPPa^t_5IxGI{|pHdk@#$DmDch=m)eX+y- z-Fk@h(N^FtN}vbt#+ri|wVpZH;K z6mJ|Z>kGn~YVpCR(g;l8G+`5E@ zq^HpJ9>ou6UqnY_3I1gFTO3`)runLg!mGk&z-G7+6tm7|8W(?DEp0qB+g9v)*{q^) zZe*vE9A`h`g4p72*RJ6@Ypl4zrxx6-4kR(T#r16tmG=+0`w?dG1W%cGSoaKbc!_2t J;m0ef?jO&ymE8aU delta 374 zcmXX?NlU|E6r8WENgIEKZ2LJ#7 diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index f93c053e9255c9fd447d4741d10564ce3d5c014d..f4ba413386cd3cbf6ec9e3effb9986b025245e09 100644 GIT binary patch delta 5010 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 Date: Fri, 15 May 2026 18:35:58 +0100 Subject: [PATCH 28/47] feat: add admin endpoint to reset penalization points --- .../New Request.request.yaml | 4 + .../controller/AppUserController.java | 34 ++++ .../exception/GlobalExceptionHandler.java | 4 + .../restoreserve/security/SecurityConfig.java | 3 +- .../restoreserve/service/AppUserService.java | 32 ++++ .../service/ReservationService.java | 2 +- .../service/ReservationServiceTest.java | 153 ++++++++++++++++++ .../controller/AppUserController.class | Bin 0 -> 2160 bytes .../exception/GlobalExceptionHandler.class | Bin 5114 -> 5114 bytes .../security/SecurityConfig.class | Bin 6331 -> 6362 bytes .../restoreserve/service/AppUserService.class | Bin 0 -> 2905 bytes .../service/ReservationService.class | Bin 12381 -> 12381 bytes .../service/ReservationServiceTest.class | Bin 5088 -> 8897 bytes 13 files changed, 230 insertions(+), 2 deletions(-) create mode 100644 postman/collections/RestoReserve Collection/New Request.request.yaml create mode 100644 src/main/java/com/pachedev/restoreserve/controller/AppUserController.java create mode 100644 src/main/java/com/pachedev/restoreserve/service/AppUserService.java create mode 100644 target/classes/com/pachedev/restoreserve/controller/AppUserController.class create mode 100644 target/classes/com/pachedev/restoreserve/service/AppUserService.class diff --git a/postman/collections/RestoReserve Collection/New Request.request.yaml b/postman/collections/RestoReserve Collection/New Request.request.yaml new file mode 100644 index 0000000..a34c741 --- /dev/null +++ b/postman/collections/RestoReserve Collection/New Request.request.yaml @@ -0,0 +1,4 @@ +$kind: http-request +url: "" +method: GET +order: 4481221878910252 diff --git a/src/main/java/com/pachedev/restoreserve/controller/AppUserController.java b/src/main/java/com/pachedev/restoreserve/controller/AppUserController.java new file mode 100644 index 0000000..b5143a0 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/controller/AppUserController.java @@ -0,0 +1,34 @@ +package com.pachedev.restoreserve.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.pachedev.restoreserve.service.AppUserService; + +import lombok.RequiredArgsConstructor; + +/** + * Controlador encargado de gestionar las operaciones relacionadas con los + * usuarios clientes. + */ +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/users") +public class AppUserController { + + private final AppUserService appUserService; + + /** + * Permite a un administrador resetear la penalización de un usuario. + */ + @PatchMapping("/{id}/reset-penalization") + public ResponseEntity resetPenalization(@PathVariable Long id) { + appUserService.resetPenalization(id); + + return ResponseEntity.ok("Los puntos de penalización del usuario con id " + id + + " han sido reiniciados y su estado ha pasado a ser ACTIVE"); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java index 9154c54..5cfbb73 100644 --- a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java @@ -44,6 +44,10 @@ public ResponseEntity handleBusinesLogic(BusinessLogicException e return new ResponseEntity<>(error, HttpStatus.CONFLICT); } + /** + * Maneja la excepción lanzada al impedir que un usuario en estado BANNED + * realice una reserva + */ @ExceptionHandler(BannedUserException.class) public ResponseEntity handleBannedUser(BannedUserException ex) { ErrorResponse error = new ErrorResponse("BANNED_USER_ERROR", ex.getMessage(), LocalDateTime.now()); diff --git a/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java b/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java index e362532..f9440ae 100644 --- a/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java +++ b/src/main/java/com/pachedev/restoreserve/security/SecurityConfig.java @@ -62,7 +62,8 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { .requestMatchers("/api/v1/auth/login", "/api/v1/auth/register", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**") .permitAll() - .requestMatchers("/api/v1/tables/**").hasRole("ADMIN") + .requestMatchers("/api/v1/tables/**", "/api/v1/users/**") + .hasRole("ADMIN") .requestMatchers("/api/v1/reservations/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated()) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) diff --git a/src/main/java/com/pachedev/restoreserve/service/AppUserService.java b/src/main/java/com/pachedev/restoreserve/service/AppUserService.java new file mode 100644 index 0000000..d2d9b7a --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/service/AppUserService.java @@ -0,0 +1,32 @@ +package com.pachedev.restoreserve.service; + +import org.springframework.stereotype.Service; + +import com.pachedev.restoreserve.exception.ResourceNotFoundException; +import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.model.enums.UserStatus; +import com.pachedev.restoreserve.repository.AppUserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class AppUserService { + + private final AppUserRepository appUserRepository; + + /** + * Reinicia los puntos de penalización de un usuario y cambia su estado a + * ACTIVE. + */ + public void resetPenalization(Long id) { + + AppUser user = appUserRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Usuario con id " + id + " no encontrado")); + + user.setPenalizationPoints(0); + user.setStatus(UserStatus.ACTIVE); + + appUserRepository.save(user); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index bbbf960..953d9a7 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -216,7 +216,7 @@ public void cancelReservation(Long id) { * Verifica que el usuario autenticado pueda acceder o modificar la reserva * indicada. */ - private void validateReservationAccess(Reservation reservation, AppUser user) { + public void validateReservationAccess(Reservation reservation, AppUser user) { if (!user.getRole().name().equals("ROLE_ADMIN") && !reservation.getUser().getId().equals(user.getId())) { throw new BusinessLogicException("Su usuario no tiene permisos para acceder a la reserva especificada"); } diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java index e89e073..45a78eb 100644 --- a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java @@ -1,5 +1,6 @@ package com.pachedev.restoreserve.service; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; @@ -11,13 +12,20 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import com.pachedev.restoreserve.dto.ReservationRequestDTO; +import com.pachedev.restoreserve.exception.BannedUserException; import com.pachedev.restoreserve.exception.BusinessLogicException; import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.model.entity.Reservation; import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.ReservationStatus; import com.pachedev.restoreserve.model.enums.TableLocation; import com.pachedev.restoreserve.model.enums.UserRole; +import com.pachedev.restoreserve.model.enums.UserStatus; import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.repository.ReservationRepository; import com.pachedev.restoreserve.repository.RestaurantTableRepository; @@ -34,6 +42,12 @@ public class ReservationServiceTest { @Mock private AppUserRepository appUserRepository; + @Mock + private Authentication authentication; + + @Mock + private SecurityContext securityContext; + @InjectMocks private ReservationService reservationService; @@ -44,6 +58,20 @@ public class ReservationServiceTest { @Test void createPastReservation() { + AppUser user = new AppUser(); + user.setId(1L); + user.setName("Tanausú Febles"); + user.setTelephone("600000000"); + user.setEmail("ertana@gmail.com"); + user.setUsername("tana"); + user.setPassword("$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi"); + user.setRole(UserRole.ROLE_USER); + user.setActive(true); + user.setPenalizationPoints(0); + user.setStatus(UserStatus.ACTIVE); + + when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); + LocalDateTime pastTime = LocalDateTime.now().minusHours(1); ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4); @@ -69,15 +97,140 @@ void createReservationExceedingPax() { user.setPassword("$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi"); user.setRole(UserRole.ROLE_USER); user.setActive(true); + user.setPenalizationPoints(0); + user.setStatus(UserStatus.ACTIVE); RestaurantTable table = new RestaurantTable(); table.setId(1L); table.setName("salon 001"); table.setMaxPax(4); table.setLocation(TableLocation.SALON); + when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); when(tableRepository.findById(1L)).thenReturn(Optional.of(table)); assertThrows(BusinessLogicException.class, () -> reservationService.create(dto, "tana")); } + + /** + * Comprobar que un usuario con estado BANNED no pueda realizar una reserva + */ + @Test + void createReservationByBannedUser() { + + AppUser user = new AppUser(); + user.setId(1L); + user.setName("Tanausú Febles"); + user.setTelephone("600000000"); + user.setEmail("ertana@gmail.com"); + user.setUsername("tana"); + user.setPassword("$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi"); + user.setRole(UserRole.ROLE_USER); + user.setActive(true); + user.setPenalizationPoints(6); + user.setStatus(UserStatus.BANNED); + + when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); + + LocalDateTime pastTime = LocalDateTime.now().minusHours(1); + ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4); + + assertThrows(BannedUserException.class, () -> reservationService.create(dto, "tana")); + } + + /** + * Comprobar que se penaliza con 2 puntos a un usuario que cancela de forma + * tardía o no aparece a la hora reservada (no-show) + */ + @Test + void addPenalizationPointsByLateCancellation() { + + AppUser client = new AppUser(); + client.setId(1L); + client.setName("Tanausú Febles"); + client.setTelephone("600000000"); + client.setEmail("ertana@gmail.com"); + client.setUsername("tana"); + client.setPassword("$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi"); + client.setRole(UserRole.ROLE_USER); + client.setActive(true); + client.setPenalizationPoints(0); + client.setStatus(UserStatus.ACTIVE); + + RestaurantTable table = new RestaurantTable(); + table.setId(1L); + table.setName("salon 001"); + table.setMaxPax(4); + table.setLocation(TableLocation.SALON); + + Reservation reservation = new Reservation(); + reservation.setId(1L); + reservation.setUser(client); + reservation.setTable(table); + reservation.setReservationDate(LocalDateTime.now().plusHours(1)); + reservation.setNumberOfGuests(4); + reservation.setStatus(ReservationStatus.CONFIRMED); + + when(reservationRepository.findById(reservation.getId())).thenReturn(Optional.of(reservation)); + when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(client)); + + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getName()).thenReturn("tana"); + + SecurityContextHolder.setContext(securityContext); + + reservationService.cancelReservation(reservation.getId()); + + assertEquals(client.getPenalizationPoints(), 2); + assertEquals(client.getStatus(), UserStatus.ACTIVE); + assertEquals(reservation.getStatus(), ReservationStatus.CANCELLED); + } + + /** + * Comprobar que cambia el estado del cliente a BANNED cuandu supera los 6 + * puntos de penalización por cancelaciones tardías o no aparecer a la hora + * reservada + */ + @Test + void banClientByExceedingSixPenalizationPoints() { + + AppUser client = new AppUser(); + client.setId(1L); + client.setName("Tanausú Febles"); + client.setTelephone("600000000"); + client.setEmail("ertana@gmail.com"); + client.setUsername("tana"); + client.setPassword("$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi"); + client.setRole(UserRole.ROLE_USER); + client.setActive(true); + client.setPenalizationPoints(6); + client.setStatus(UserStatus.ACTIVE); + + RestaurantTable table = new RestaurantTable(); + table.setId(1L); + table.setName("salon 001"); + table.setMaxPax(4); + table.setLocation(TableLocation.SALON); + + Reservation reservation = new Reservation(); + reservation.setId(1L); + reservation.setUser(client); + reservation.setTable(table); + reservation.setReservationDate(LocalDateTime.now().plusHours(1)); + reservation.setNumberOfGuests(4); + reservation.setStatus(ReservationStatus.CONFIRMED); + + when(reservationRepository.findById(reservation.getId())).thenReturn(Optional.of(reservation)); + when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(client)); + + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getName()).thenReturn("tana"); + + SecurityContextHolder.setContext(securityContext); + + reservationService.cancelReservation(reservation.getId()); + + assertEquals(client.getPenalizationPoints(), 8); + assertEquals(client.getStatus(), UserStatus.BANNED); + } } diff --git a/target/classes/com/pachedev/restoreserve/controller/AppUserController.class b/target/classes/com/pachedev/restoreserve/controller/AppUserController.class new file mode 100644 index 0000000000000000000000000000000000000000..7199f6d74356d49bae6c0008913ef1a353d87462 GIT binary patch literal 2160 zcmbVMTXWM!6#mvEQKA|W+!9DBARs_41qE6tt&_CDO(3n)WN_*}@U)f|d$GK$N-HyI zr~jirqn+Lu9{SR^{-{omEXS9L$p8=X?m0fU{mysJzyH1b8^9XAwvc1E=joOka<3tL z(RL#d8%;kVY75uX%0xN{MC5LS;kTqL^)wd77-pXFHg^NA>h5msneYt5DV|wYD1!6^ z!=3W06QpU#=rDTMr^s+REy3&w#RK^RH&QEx;zGHfuB=tPxLDRv-HpRYs`}H2x5RTD zHQk0Wq5D|Gp;ocjR7RQuia1q~b;V5*QAxXRA8B=j~lPCLja~>8OGZ@NT}R%?vK*{DQ%4~H^U)Jt_cSEW^s1wloh5=P1rm;Hx&?#|IY9 z53wnOHjfLq$Y6DF*nP@yeb7H+9Fd^Uif%^=L~kdL%ecaDw#A!*XzOwFoiq))#fB?G zeHjYY^&An*#yqZDxW;hfHQAQO4JUCCW*kIxv7`XTnIeBTPg4J!VmsiB+kjS!cMra7?~e%=TiH z;e401+p0ZcUJB(vw`#iSJ`_qs+z7vD;{@*Jv4VRHCl=BWc10(VC$?k?$?TEsbSvdV zipMCVM~xos3)5e2gvXn>exHzce{N5@Jd|$xwwn+JgxAaitxaqq9%fj^HrDZlh0hu0 z``nePt((Hlh?o&}i+i+;4^k#?F#J47rLJ`r@DW#jAmaJ5*3Bd=4m7b#>jBM>v_o8U zr0+1N!IcwBUptW?PI;31kT$WaP-9E;e( zLxyWdpd1B`g>8n5$D0zvGb9Oj;u|d!n!0oW(k(}K3zH;GbtD__(0PiqdHPO10gFkR zCY{AdF+-Qr^zD#ij?ObnzoW4HC*E6rfw`Bs1iZl2U+9k<7U|dS7+s+Fm#{?gZ03Yz zd`J?x<0fuVSeNeOjD)nb72KvG@6dgMq>t!KGp>_sn$ceR3m-2n|AE4tuU)8gCK+XtuU)0gCK+P11iCi5KYsai6a(Bno3P*T(AMUcncI;?4tw?ia5{H zb)H9@!CI(OG^|{>)1`@tzr=XjVqBbi&-w0m?vVQ_@-t$5`}6S&u#pdW!}N!&aB5^I zJQfM(96RfT+uN70U*YZO?1?1|vP_yBWJ-tpGvULEhJSX}7JV!=I+RIQnvNIS!b+|;XrZSLFL(edDK zbzQHiYutX%K=IIID^Y(H4=kb4vsR?`H=4+=20ApC_;{S!8&DY+$0u0Z(P&&>~y(j+DF!VaJM`61B+L zyzJ#BAY+K?@@M$iL_M2nmbsNJtfhnX^7d|`4AMzZEVFWlaV~IC%J_dIb%7 delta 528 zcmYk3%WG3X6o1H)m_Lby?lFb6A=9`1U-N|KB&STXGijaCoHaij&pNNUQ2Fho z>S8y!9p{!=?zKnmaM$?WuiOnq!;=1-zt````-SZt>$c{y+jg&5y3ygjjpC7}hh!qg zLx*IIJ=YA77WT{`U1P5`n;8-|>M*3xsX`%JO6L6_$OduVDHfYCugOe%v3WsTf3dh@ z&#pLSMawLn8z^G4jme3Il8W}yN?&DPW0$I*A}W`GtohQ^K4ML=WiaCI&xGW^QF4bv z4%FD;8jH?Yj%EfecUai*kkeufIaiirBDj)UnNxk?IIC!6H7%lTtYI15vU=D=5>Fg@ zW;7SMB)hc~|3{YvSzM7^$5rY@ZK;gtHHqsIi)7qAi3dF9Nws>)3tm>MSG?gZW8zvM Uj&a^IK{Gq4@PYaApA|yXUtfNBX8-^I diff --git a/target/classes/com/pachedev/restoreserve/service/AppUserService.class b/target/classes/com/pachedev/restoreserve/service/AppUserService.class new file mode 100644 index 0000000000000000000000000000000000000000..8f2c69dbd32efbad43c3043143da877cef4cc5ff GIT binary patch literal 2905 zcmcIm>vj`W6#hZ;|od3{YZ zM8^$8s1^N*pd(!K@70An+iu^Ybv4;q=x4~^;~nn$TsGZ>m3yMD8TRr-Y)Q0L=#iBT zhI5r?TMwSdbscPJmdKomLN5x*eeWBtJtZ0T6)RhQD@ry?6E%kZIn@wDVA$bk@kFdp>5*hercEryU)RX9AM~c z3aW~mRZT+9D6U4bZdBcB)NcEp2ud~%V${MRhT%BXZT20!g2R-j3aot#x*(=qZo0}Rh=Mm%c z85CmHQm7HBQ2n1HYJ<5MpN=4vMOOcb|imzHLYTdmmqzJec4MMUHA2_%KB0^u$m>BeVYo)=*Pchug zqLB9Lv%5rmT^DiRyGtTeQBW5*l%7|SY+UKyP^Xm{F1(Np8z14ih03;>Th)Olo2Kt> z{4;qHVVX@0iws;UV^ct-~&Ex*t#>7-J+so;ZjcZsob-YEboGn{d zg(_=T5aB<)Ax}f z*kz@3r~}?km=@aj6rWr8jN#bUwDM#}tqZpj<4uBmmDkN{%E1@-lBOzgvwOtwM>eTc zGckUR%Z4w)@rqLGQM;6Blr;Cu$u`Ah3OXzP{FjN#-nv3r9A_<%do$g zl}V>e9xFBxLpYenRg$CLpC!2$qHHOTrj2{>Evz$)KZQV2r-XjKXXVeD2I(X3U4x{?L2QD6nB3PRG3=+{sTPFd2hne%xViqHa$q2o^# zU?(D3h{$?xQJR|x>u{7X>!Xhi8gk~FhgN;GwlIhshG_RKJ7ih@#!B?+{8&EHT?*ubGZEjY2|R1{>R83a0oU=F+$ISI7}Y_N9g@M zh8bGDL$`&*3(nzP+L1rJhZ03%WY5z#W)2tWbQhyz6YM3tPYScN9-!T2dYWJ_l30$O z_T&T1O-}s`dn*Vd2YO)|XCsN!6K8guI_tXFg?!#NG$s{eb|$U~u=f{SyEFL^A3nwc z@Br2C=`u+%u|67GHxcxs7T?{*$MiI}cj);E{f0?{lfgPxu?9&u3SZH=X`TQY9gO}3 Dg4tLj literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index f4ba413386cd3cbf6ec9e3effb9986b025245e09..56655994b36586ce247248ef0bc3113dcdffc43b 100644 GIT binary patch delta 14 Wcmcbca5rJYE*(b3&AW9LF#`ZLjRsNx delta 14 Wcmcbca5rJYE*(au&AW9LF#`ZLlLk`& diff --git a/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class b/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class index 43f070d31959d57215118000ea8918e56c73c794..122a33051cdec686adc9ea17b7091b9906acf4d6 100644 GIT binary patch literal 8897 zcmds7d3;pW75;7rykzo}5CM(2prR&(VIqp6K_m$QqLalW1Ob=6%)BHICU1sWfVl5k zq}8_Wdp8%fS}7d@v}$YJS6u2+t=87sx^-{u-tXS`X7Yw)V!;2ZKW5(Cckem(e&;*O zJ@C|jwmk^ocyX$R0)aW9XkQ>^gnG@exh4=d6Uit)%=j8JAU~~;8E8|#jieQ|+nu2f zPSa2*P`c7sV+10G-4kf(T4{!o0+XEOYI>U)izX~e>=&3D%&#e)o#$0HxVlE5ye(xX ztv<8UN?2VHv(~nwNu?tpP!^2FdjfsY&?@Q*H1V4Y#wQKV8<@%!n?P;O8hO&p`}(MzG}qAvHKPy{awZ4bUE%Ix$}96&&^5Tg(IcEPKT#;HJjXbV zwq%JkQG)Nr&9!VRIZt3rMP;WzVSO}g`Y;Cj=$ME}JXz4P&E`~Jml;>KP^JzUkxnCS zN!z%Cg~?uvG&pXzwkWyGtxO8VO(SWx8i}Nb?*jX&?XOI6pMjOBn3Xi+0V8Gw)G`fI z1!m^wbsx_Y2}lFecrF2aI1u~za0m|7FpU%#^q^qW?$L1=4j0hY7?G6O(k-x8#fg={ z+&MK;M5T`Ds1g{PFq4g8IdA7#m7PWK6T=R}Hd2X4woP4V60(F`9MDmXBdCUpo5?aB z-@Cj$Dfw3;1so|*d~~(@pIkay$I)`B&ZQk@#EkVuZLPvsx|Sx zwXu#x4Q)#k-HCd0X4~xLjjhIst@W+Pbo8xE*lkVAk_~f~w6E<+SnDh)d4Z0GjAR-$ zK}QM~V-XrPEFP83QhkYla`kA$)NvwCA`)#a!G<%Iv^TT~RF6QmjM=>+l+l`WG^54A zy^V4Oj;z>KIhCD0oQ!r2+{A=IV$?+`&#)zbIs_(HEcdd;eW~`)sbeWl;b91QZ76B2 z;Vve7t)H1J6)gv|#EKk6jf^opW?U*dO|ES8Dso>cMQ7;v0t`}%ids$Eh*)Q;DAyXb z>?GYaM`>nZWoI!$Fg1|U)t~KH8HC%J3sMOk-RNOdsjcs5>}-(HbXO&)jaxcaVwHoC zBUoVeuGq<0ME@yK;Gik-71v!;%9V3ICe%XGv@>J{oF_L|C^IG#wRy1E!7 z0xfnA?b*}r{-&Ce>G&enGYi?#wE|NrTt*L_ z$J1Sk4LZJrFH=LGWv3F0qp3LUvR^h#53@?cRrK`yb`P^^@g$u#b9IVIX+cMe4_D%= zG8cYL-~_KtWU8oftUQc9PbL`jD-GAsu=zJ(t_zupD}lOHf@wFA2u6FXP(x<8j78&h zIV=%U&LwY! zVv$s0fzhAPaLKD;xQSwXA6qn}NA#+UQkf%Ep(tHm8e7Kzwz6_+uMM^|kBn1Z8RXVRY}fGv{Lrah zGQxSI8p$Q{3v{x@LpqA#E5VQOh=v~v%**evo@}ubq=UB;9Y2u)x5&vFjo~sTr0dAQ zFasYENJ>U>$0IN$v*F=PN2(1aOI?4RVcTX{rYM=qpVsk=%F1=M&CLx9G(0D8O#V}O z!cbO>Ck7SB?H_K0!$T{Iy8a+fRd3iKGZImT*6<>Wfc(04J{vE$YMl=|BqevCQhxkY z$E$eFDcaDYFU8Mwye(a^Sh}LhMN-i( zbi9jSG6mCPJbouDRq1yg z<(U%gLwv;T*0(e-Y;0?4SRiohDA+fwEc)OMd?Jgr-|@mQ8X$)k<9Oa5bd1F~De_M` z{*1rSJ$jt0RavGWP<;-SdQ0#(>7bvEm{D^bG()aI=akjqKXi;oX$k&?|7iF(!H|h+ znX-0OiY_!!MAzY_ zoO|ZP94y^>)hB#H*F*`6oX=K}=lF}GkuYy1Vh^z=OEWUp?PoBAR*d5Cd89fuyeQQ% z0cAc>CMIfPF9MoBrZ7O{X`1O`lDx1_2&o9=@eE#9#ooHuN0jrjM5Km>HhDkyRuh@3 zjs=dGqKo~+{@K!{VRg!guyonmTe@WCH%bRTP#ma>sp24dA(dq=IFcjzPv!M#83WxL zj?{mM)IW_u*{jg4=RJjJroxwjP#h*Vz&1zCsFix)a&~i;z-`sRGp_nPqv7ZOq{KYqs1}uysB{}aNbA}FuzWZjk4Fr{6rWcClO=y zZ1S!m%TpHs+3#^5XYoMoyds6ng_bNerg#nMxQe_B%#?=|n31R1#-^reC!Fe@PvNCj zvX}bCsxDb(S*HJ+Tb#>QBk#8j83|lg$__^|k&GL$CNtR^4JUk}R@7^PZKs0=!M5x* z(N(-BDw}em(QSleCtDW_L<8B?H#j8ljF< zoP{0zG1EJJ^e-KiBd_&F6IXJ*5s9?3zgaVWoS0vVNj@fkMqLC%^>|Gz=72latcy8n ztZ8tpT^Glyu{Mq|7B#X0jH?TiNfxu)bm$OZhl58G%LNV?c`6z2V2Oj6?#A#|It~Rg zb@P{|evy6rE|7)vkTJGmhKw=A4H;un9x}!h;2GPCI%EcCqne+S`76M0!SAutw_xuF z)E3J5DK`cjjD7hjm2(27U_X8X`*U$YcH0MVgo4C)&M4&b729#(vMo3$SmhhQ!D&=f zmCZwj&G>)n&J0^`|tIFo4F|VquHjVnKO<0&lu&QtXt!XT&8Z&@pX`IgCt~7e5 zZ$_kS?pD|Zy#6Gs25{z9oL_*Y1GvPO#uZK5apf|pW`@RPTkw_Uva6^2x8YiF`i)Bm zk5flv_}53ocpjfl;D+b(Ftt2j9V$?dnOK0??7be3h5THEAQ}?29JW8#qeVe6 z-8rHQMG-E-?f5$3iLiP54SbWZoI|^O3*S~YzGGOR))J_(+|3T>5O)SMho~x@=$wMb zQV=%#T?qXOLcfC0uORd*2>l8|zXPG)h0yOp$YBZLc5Eav_a0YRUg${sPI+P3eQA8R zyikIc#slSro3J^JZMk6kvtV=lLAi7*9#&>3ry|!5gnPy|Ji0U7%N4awA>7Ld+X}*b zDjWZ&v44L$8}VmgE}N`%Ft8Y11UAIpZkVH{?1ZxA*vqz_h4WYHXcC? zFJQGY!%+@^Su;F|#|XaQyDbX*0&2WpnPv>-b>VS5p$zxrum&l0nY-chF}HMFmby>v z7ImMM)O~(uEFJ3h6U#G6-Lr`7*`)3{JmI;d?s=r{`K0azr0#_z??q_BB}DQvqINlF ztj87nUwW}a-3?jHE_SKgm!1 zINaQh9c1xK8IO3`UzNryeg>A;$x|5%-b&*gzeDI>k@@eZ@iBS(dw&{#+={;{-sUiR z*s#JCf8UCK3K?vsZx+G{HtI=?QKK>#$p9p04v1pD8ZT9(#e}M~m^^ks?91;1=y?O; zV1HU1x=B>165YhFuhUPo(QPix*lYu-p}-b8EOOl#i4Ky)j^&+X`7 zh&h$vrHdS2Nq(=!9q7lMwE11UOy5oWZlt7pIQw1(p8N0+x&JC2z{l7m1U8Ez_MG;@ zfSAGt(xKQU0@yyt2vMD3gs2czDwuWPP318K)Urj)5PliV#LQtC@sZ1jiqFRgkMDS5 z*8c+~N{h0bcq)ep7h)=z@F6E9;ZOms3M1T zmYB;w$FG10iFx7#zMG0MVj=%55+{n2)KgHjh*tGs-VsxokCRU*Qe~y+f AUH||9 delta 1890 zcmb7E`CAiL6g`tnGLy+9U(wWPWEv6wu z#uQ|-g_h}jHir^6U`kv< zBOS3+pi;mbj`$TBolLgM#<`fsQmt+5ly!$Q4GU!~LM8{X)7KNIQhEad7PER|iOq`y z30~C6Sc-af(U?B5fkRxAqA5Lr^P}lC>N=kq&^?yRXu=96@tL7!4wllw*iXk+%UFXI z9OQr+Y*Kw{=SIJ#inx_SyxJY4So7LCly=|r_5Kds{dO65=hO9Q ztvYNactt?%)|3tkwt+)MX0ei4RLqX@Ws|(vD5L$l+cG5(*y8VQwct*42=Gy9Su9Qv zS)s{n)Zk~cO!FOHbjj#OfP+Ah8~r|&&5z58x$*3b$>iz576F_8))SAP92HIP$yN@| zscUj;#~T&{%XQj!$+#Q$Fta(!EZ|-arEDOsbd`iYUEw~|iP%oM1r(oOOD`-e(rwH2 z#|{|};6WWygLTT*WlAK?4M}2CQJ%Zff`{>lfSu8a%Vs-mHj|7;@fZ_q94B zrhqp%RI+{{i(Rp$rX0sX>h*?pt<}@(ZFP`u8Fjl=394+HwSrmg3)q^(MgvZ*N!1;wKR;Jd|?>wAr#j+W`^>njp7ziLFLWkxg6kWUjalE zQtV=6qa>0yJsK~TSHxt@hlet{V8;Sf5eK09zqoKM#KlDDxhR*K>s&%P`SA%cHe>Lv z9>S8PZk=NowQk3S6#yYn{4WrF{0-M6%1`$q7J0nG#)(f5wv zs1EIQQRo=tn5pY@;-5M-B7thEAf4ur>T{8SdB`J83Q3hxQlx@3@sJ{mP=k6bL^B=b z7A&T_WeGY_i(Z84&`-DH9)eGz0cX(|5qd^ctiOaF#aj`fQ*bCIbO!d(j<=~HEm(&z zhQP<`tVgH`qeP0Qwv0u_U?%zKjj>XF@8s(Wo*D0SC^}ZfH;k1vnw6r_ME}2N(O?b# z4n}l7APNS27@1N#k^6}LAFnL}w;mVp34u<6za*}g@io4QtiHt$_z^!5rXRbXaSgx1 F^(P9Pj28d^ From 4107e35f04811e9e6a55eede5dbe57d82aaa9acc Mon Sep 17 00:00:00 2001 From: dani Date: Wed, 20 May 2026 09:09:50 +0100 Subject: [PATCH 29/47] feat: implement VIP logic in Service --- .../dto/ReservationRequestDTO.java | 7 +-- .../exception/GlobalExceptionHandler.java | 13 +++++- .../exception/NotVipUserException.java | 8 ++++ .../model/entity/RestaurantTable.java | 2 + .../model/enums/ReservationStatus.java | 3 +- .../service/ReservationService.java | 44 ++++++++++++++++++ src/main/resources/data.sql | 16 +++---- .../service/ReservationServiceTest.java | 6 +-- .../dto/ReservationRequestDTO.class | Bin 2741 -> 2913 bytes .../exception/GlobalExceptionHandler.class | Bin 5114 -> 5702 bytes .../exception/NotVipUserException.class | Bin 0 -> 458 bytes .../model/entity/RestaurantTable.class | Bin 2605 -> 3007 bytes .../model/enums/ReservationStatus.class | Bin 1326 -> 1382 bytes .../service/ReservationService.class | Bin 12381 -> 13253 bytes target/classes/data.sql | 16 +++---- .../service/ReservationServiceTest.class | Bin 8897 -> 8928 bytes 16 files changed, 91 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/exception/NotVipUserException.java create mode 100644 target/classes/com/pachedev/restoreserve/exception/NotVipUserException.class diff --git a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java index acf36d6..1465f99 100644 --- a/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java +++ b/src/main/java/com/pachedev/restoreserve/dto/ReservationRequestDTO.java @@ -7,7 +7,8 @@ import jakarta.validation.constraints.NotNull; public record ReservationRequestDTO( - @NotNull(message = "el id de la mesa es obligatorio") Long tableId, - @NotNull(message = "la fecha de reserva es obligatoria") @Future(message = "La reserva debe ser en una fecha futura") LocalDateTime reservationDate, - @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests) { + @NotNull(message = "el id de la mesa es obligatorio") Long tableId, + @NotNull(message = "la fecha de reserva es obligatoria") @Future(message = "La reserva debe ser en una fecha futura") LocalDateTime reservationDate, + @NotNull(message = "el número de comensales es obligatorio") @Min(value = 1, message = "El mínimo de comensales es 1") Integer numberOfGuests, + Boolean isVip) { } diff --git a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java index 5cfbb73..c799abb 100644 --- a/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/pachedev/restoreserve/exception/GlobalExceptionHandler.java @@ -46,7 +46,7 @@ public ResponseEntity handleBusinesLogic(BusinessLogicException e /** * Maneja la excepción lanzada al impedir que un usuario en estado BANNED - * realice una reserva + * realice una reserva */ @ExceptionHandler(BannedUserException.class) public ResponseEntity handleBannedUser(BannedUserException ex) { @@ -55,4 +55,15 @@ public ResponseEntity handleBannedUser(BannedUserException ex) { return new ResponseEntity<>(error, HttpStatus.UNPROCESSABLE_ENTITY); } + + /** + * Maneja la excepción lanzada al impedir que un usuario reserve una mesa VIP + * sin serlo. + */ + @ExceptionHandler(NotVipUserException.class) + public ResponseEntity handleNotVip(NotVipUserException ex) { + ErrorResponse error = new ErrorResponse("NOT_VIP_USER", ex.getMessage(), LocalDateTime.now()); + + return new ResponseEntity<>(error, HttpStatus.FORBIDDEN); + } } \ No newline at end of file diff --git a/src/main/java/com/pachedev/restoreserve/exception/NotVipUserException.java b/src/main/java/com/pachedev/restoreserve/exception/NotVipUserException.java new file mode 100644 index 0000000..74afc08 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/exception/NotVipUserException.java @@ -0,0 +1,8 @@ +package com.pachedev.restoreserve.exception; + +public class NotVipUserException extends RuntimeException { + + public NotVipUserException(String message) { + super(message); + } +} diff --git a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java index 26878f6..e6571d0 100644 --- a/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java +++ b/src/main/java/com/pachedev/restoreserve/model/entity/RestaurantTable.java @@ -40,4 +40,6 @@ public class RestaurantTable { @Column(name = "location", nullable = false) private TableLocation location; + @Column(name = "is_vip", nullable = false) + private Boolean isVip = false; } diff --git a/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java b/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java index 4e72c89..966e07a 100644 --- a/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java +++ b/src/main/java/com/pachedev/restoreserve/model/enums/ReservationStatus.java @@ -3,5 +3,6 @@ public enum ReservationStatus { PENDING, CONFIRMED, - CANCELLED + CANCELLED, + COMPLETED } diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 953d9a7..6adf05b 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -12,6 +12,7 @@ import com.pachedev.restoreserve.dto.ReservationResponseDTO; import com.pachedev.restoreserve.exception.BannedUserException; import com.pachedev.restoreserve.exception.BusinessLogicException; +import com.pachedev.restoreserve.exception.NotVipUserException; import com.pachedev.restoreserve.exception.ResourceNotFoundException; import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.entity.Reservation; @@ -56,6 +57,16 @@ public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUs RestaurantTable table = tableRepository.findById(dto.tableId()) .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + dto.tableId() + " no encontrada")); + if (dto.isVip() == true) { + boolean isVipUser = reservationRepository.findByStatus(ReservationStatus.CONFIRMED).size() > 3; + + if (!isVipUser) { + throw new NotVipUserException("Sólo clientes habituales pueden reservar mesas VIP"); + } + + table.setIsVip(true); + } + if (dto.numberOfGuests() > table.getMaxPax()) { throw new BusinessLogicException("El número de comensales introducido supera la capacidad de la mesa"); } @@ -242,4 +253,37 @@ private Reservation getReservation(Long id) { .orElseThrow(() -> new ResourceNotFoundException("Reserva con id " + id + " no encontrada")); return reservation; } + + /** + * Modifica el estado de una reserva a COMPLETED si han pasado más de dos horas + * de la hora de la reserva. + */ + public void completeReservation(Long id) { + + Reservation reservation = getReservation(id); + + AppUser user = getUser(); + + validateReservationAccess(reservation, user); + + if (reservation.getStatus() == ReservationStatus.COMPLETED) { + throw new BusinessLogicException("La reserva con id " + id + " ya estaba completada"); + } + + LocalDateTime reservationTime = reservation.getReservationDate(); + LocalDateTime limitToComplete = reservationTime.plusHours(2); + + AppUser client = reservation.getUser(); + + if (LocalDateTime.now().isAfter(limitToComplete)) { + + client.setStatus(UserStatus.BANNED); + + appUserRepository.save(client); + } + + reservation.setStatus(ReservationStatus.COMPLETED); + + reservationRepository.save(reservation); + } } diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index d657bae..1ae8510 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,14 +1,14 @@ -INSERT INTO restaurant_tables (name, max_pax, status, location) -VALUES ('Mesa 1', 4, 'AVAILABLE', 'SALON'); +INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) +VALUES ('Mesa 1', 4, 'AVAILABLE', 'SALON', false); -INSERT INTO restaurant_tables (name, max_pax, status, location) -VALUES ('Mesa 2', 2, 'RESERVED', 'TERRACE'); +INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) +VALUES ('Mesa 2', 2, 'RESERVED', 'TERRACE', false); -INSERT INTO restaurant_tables (name, max_pax, status, location) -VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON'); +INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) +VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON', false); -INSERT INTO restaurant_tables (name, max_pax, status, location) -VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE'); +INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) +VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE', false); -- test credentials-> username: armymoves - password: 1234 INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java index 45a78eb..c1c4ee3 100644 --- a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java @@ -73,7 +73,7 @@ void createPastReservation() { when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); LocalDateTime pastTime = LocalDateTime.now().minusHours(1); - ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4); + ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4, false); assertThrows(BusinessLogicException.class, () -> reservationService.create(dto, "tana")); } @@ -86,7 +86,7 @@ void createPastReservation() { void createReservationExceedingPax() { LocalDateTime date = LocalDateTime.now().plusDays(1); - ReservationRequestDTO dto = new ReservationRequestDTO(1L, date, 20); + ReservationRequestDTO dto = new ReservationRequestDTO(1L, date, 20, false); AppUser user = new AppUser(); user.setId(1L); @@ -133,7 +133,7 @@ void createReservationByBannedUser() { when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); LocalDateTime pastTime = LocalDateTime.now().minusHours(1); - ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4); + ReservationRequestDTO dto = new ReservationRequestDTO(1L, pastTime, 4, false); assertThrows(BannedUserException.class, () -> reservationService.create(dto, "tana")); } diff --git a/target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class index ed14c1e3ba334796c5f15b70f660a56b55f418fe..bde70009ad75085f8c858d19ab0edcf492a34408 100644 GIT binary patch delta 869 zcmaKq+int36o&sjN5HWaOpR111vyLyDz)@XQ45}^*kY|!FXsXcN`@-Y3oq{ zQcP|4cG|lFXB*G#*LJaEJ5P(%Znx92oiaU^CdCB3kv7Cd`YI*Otbs5h0%PXXkoF)1 zrVKcXT3b@hf;p%)BqGl8ZssH9Bl_;dww+IM~wkAiX7kS zM@&C*`VSK3+2l`<9Lo^=blvKs031Vme}#zws)(5()Menkr+qjyhxrr8YuZJ|iYhM51Ku5+3=1GP z*lb?qdRXyb<0xc{vMIR6tAsUF*+=-Pb#J8WXy7h3u!$zuLtMXy`*^_24(ARO+6+C> Hj(?Tk;<0pR delta 694 zcmZvaPft@p6vfZHdGF0@c?N1w#A0bFJa7t9DFW3ZY5^_sr;wN!V`54|!oskiD_172 zT$#&BJp>qcTKgvAYo1$m+8<(G~U zGa;r?QdpxhC%f*jT9P;JqWo|R{z8aZltp__{UzKtxF-#7R6jsf-g;ln-2kf!k?i@3szpop?n+)|3fJni+E}W z8KUf-F2zFKAW;d}Q$NfGO^IlV{77Zx!EV-9F{UT26eMHryBzz#&AKxMP8A8{t0jaml)(BZLbzCn>iyK z*NPG@W0JvIESn5#`81K3>$`mlcLdz#KU@LpH12YT`)BwTLvv@WHf(75!Ekctke)TW z)RcnzA{ZV?IEhnkJ03EGI%D;vd_DzF=;mlXuMc+{TB)R$1k5lj=X>0(d`b~yf2>g0 zT`mfkWmr;IRVKXttjt)l1m2GWutA|evEU%Qb;u$Gk>U)kCc!@To Jp<>SN`~$oal|%pl delta 327 zcmWlUIZFdk6ot>5$c!1E$z&vgHi`%_aY?OW6%{n(2UrgH43Qy-6D`yj_kD{`;}(~= zbT%RgT8Nc}rHHlu0|hG!@uoPO%XiPYaG#rg1IEkW!y|wWTxn1k+9cEKv+tERy{}xp zd7|z#G8!fsg7VuFjB-q&hM_brQr!t*_v=VwhGVt}eFQtjfHh*LV#0B(xG2Ls7Bws| zln+{?R(K?pu){HHh+_%MGVlFSvyd9LWzGn=cCag-j1RUY3%=@#Jsk{(9Ag;wsyJY1 zX^>x@X1{?v1Mf*D#c;&797i}B&ZJ?6*tu*sBTT)`nAb|3p%FbJ;K(0{G%HU`X)K&6qV#&jGwv7Zi0!Q!&6v=RO`>K)Wp#xYr0=FWC!ECp9KA~jQqFTV`RMR#HbU!)-PDY) zl~pM_w$d#en8GG}sjIPe{`ipN$_INOS*SWBSGIzOKu}MzC%Jxj^n~R{sXT-d!%tR} e0agWXFK`2Ef<%vXG{vbUb&Uj6#v2knwBG@hg>`%Y literal 0 HcmV?d00001 diff --git a/target/classes/com/pachedev/restoreserve/model/entity/RestaurantTable.class b/target/classes/com/pachedev/restoreserve/model/entity/RestaurantTable.class index 08bed5b65c2dfbb13849c6edcea2364e4ad16d0a..4b995231926662027a5d0b952ad464ee735ab0c0 100644 GIT binary patch literal 3007 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 delta 524 zcmZ{gO)mpM9LAs7_g#avrS!!+E!$E>dm`a1PJ7yeLk~hkU5Lb~e1zJAZy~xpv>_Td z;)D1Q4&pzf;bfE9|2*@b`906fhjZ^Z?_ZBS0NbcGomTVp*l;;U;6)Xd^G7*!7#2($ z4C5T85S9>W4hx2uWjSm((Jd=5H|nvQsXUfVEHStzwaZ%Rw03q>3a-vC4(l8~3ekab zXOuXKSc^WDw}!Tp5*`3m!pllzz)cG-CDX`h0GzA_N!3Y2HBfgF_dFP>GO18xodJOst?=r2c zR87B-?O^(cAOHr1L;sKnN?R}^E`fO(wt`s~n4@810gL^cRLM1l}rz-p$kNI7!zJUFIPGr#;%GUzCF7X={Hsz%94P*a3 AHvj+t diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class index 56655994b36586ce247248ef0bc3113dcdffc43b..22209db70db3d870e1fadd3c12b72be5cc4ba1eb 100644 GIT binary patch delta 5262 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 Jye@CR`+sgTbnpNG delta 4425 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 username: armymoves - password: 1234 INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) diff --git a/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class b/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class index 122a33051cdec686adc9ea17b7091b9906acf4d6..a6bbe003f164f04162f468181ed53f801aad5e2f 100644 GIT binary patch delta 392 zcmYk2%`3xk9LHa?4ciLE#cvOqWrbL3Cr?SuX(7s59)FlIvypP4*d#(WeK~v1fudIY zsDFIJ{UJmF(k>`fI2rD+Z zm9ot#XXuFH;+zYLNQfK7=~|U`MydGH*fKwG?SCc}G;7p0R#a&ObKc*|$GU@I+>Fr7 zC|<^BWt=W1=t0HLB!f&z#k8CuB7|9HmNn+sl-oY@9I?PHG2U3@lcgM^H~+c7_|B^& z)8XKeORglNo4XvhzKCVfZgZNd{?eyq`c9Fpv4}fcOTK}uE#OJAONzaJ4WtY1JAVx< M6dFioX6(bpU-(Z~wg3PC delta 356 zcmYL_OG^TA6vcmPQ_U>Wpcd+gxZG9BdNgk}I5KXyck2T%zBqlD9LsE3TSs zUp9H;iMz2^PUD76DD3I5iK}LRCFdhSh$-=(rjHp0m?cV*5#|_WUeXt^Sd{A$%dC)Q zl|9zTvCc6YoRQ+5E#BFt!cLX7zn0oy{p3Rk9ilx~37wMoP~~?yNh{0j(mnMD6y*W? d>esY-vr@ev^IyK$Fg&W4Pc_PC%lSY;`vbpnNvQw; From 8de725939078290cb8736c43977278c1b4fd3a63 Mon Sep 17 00:00:00 2001 From: dani Date: Wed, 20 May 2026 09:59:58 +0100 Subject: [PATCH 30/47] feat: implement filter endpoint --- .../controller/RestaurantTableController.java | 10 ++++++++++ .../repository/RestaurantTableRepository.java | 1 + .../service/RestaurantTableService.java | 10 ++++++++++ .../RestaurantTableController.class | Bin 6737 -> 7385 bytes .../RestaurantTableRepository.class | Bin 1499 -> 1819 bytes .../service/RestaurantTableService.class | Bin 7937 -> 8406 bytes 6 files changed, 21 insertions(+) diff --git a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java index 72b5168..dd32c11 100644 --- a/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java +++ b/src/main/java/com/pachedev/restoreserve/controller/RestaurantTableController.java @@ -114,4 +114,14 @@ public ResponseEntity> findByLocationAndStatus( @RequestParam TableLocation location, @RequestParam TableStatus status) { return ResponseEntity.ok(restaurantTableService.findByLocationAndStatus(location, status)); } + + /** + * Devuelve las mesas filtradas por estado y capacidad. + * 'localhost:8080/api/v1/tables/available?status=AVAILABLE&maxPax=2' + */ + @GetMapping("/available") + public ResponseEntity> findByStatusAndMaxPax( + @RequestParam TableStatus status, @RequestParam Integer maxPax) { + return ResponseEntity.ok(restaurantTableService.findByStatusAndMaxPaxGreaterThanEqual(status, maxPax)); + } } diff --git a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java index 0b26595..028564c 100644 --- a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java +++ b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java @@ -18,4 +18,5 @@ public interface RestaurantTableRepository extends JpaRepository findByLocationAndStatus(TableLocation location, TableStatus status); + List findByStatusAndMaxPaxGreaterThanEqual(TableStatus status, Integer maxPax); } diff --git a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java index 9af649e..a3d3a60 100644 --- a/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java +++ b/src/main/java/com/pachedev/restoreserve/service/RestaurantTableService.java @@ -139,4 +139,14 @@ public List findByLocationAndStatus(TableLocation lo return response; } + + /** + * Devuelve las mesas filtrando por estado y capacidad. + */ + public List findByStatusAndMaxPaxGreaterThanEqual(TableStatus status, Integer maxPax) { + List tables = restaurantTableRepository.findByStatusAndMaxPaxGreaterThanEqual(status, maxPax); + List response = toResponseList(tables); + + return response; + } } diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class index 36e0b97ab2222689db3765f137d90177dcb3cb73..4278fa95bd5937da312c20ccccafa77db54e7061 100644 GIT binary patch delta 373 zcmca;a??`a)W2Q(7#J9A8D_FGFflUNO>C6fsHG_^DVmmhBrL?13$|`@pl}0QKMz9}!-UD#h5s;eZ@wb( zoJE$EffMLtCWdAPZXnIaz{p?@#1_#EjKvHslN}{BMO$|;*lhr-(E^feKv@Qc+R62j z;x0-I`CuL|LnlKz&Go~_31M)%QstocB)7dx~qJi`bHckLKzf*_+ delta 161 zcmcaC6fsHG{)#b%S4msw)R$Y4A9p@_`nyG(ME zJ4CJ6+IScm8QLeG5dFi*wfU&na~64KhGw9CHU>t979cGE#C8l!K*+$rrL~=*b#ju7 wgLwy#$HdUdzzw8@fUGVc-3`=X5zW9@%+SNo3l&#okZ0&)<79{i(*10l0P`LpSpWb4 diff --git a/target/classes/com/pachedev/restoreserve/repository/RestaurantTableRepository.class b/target/classes/com/pachedev/restoreserve/repository/RestaurantTableRepository.class index 7512ccb12faf0e6dfc43ba7bd96fa355208a8e80..bb328c3ab1fcb48497041abb1781c5dbe7e900af 100644 GIT binary patch delta 168 zcmcc3J)2M9)W2Q(7#J9A8RXd+m>3!CCO0xlZPbcq(Nayz%u8{q3@%A5DJ^!)OYu#t z2uQ4OFG@`;Ni7P=NX&CBEKST|WQfq1%*d)dc|Ehh#Ba*1){G33kOUIg*>b`9CbzR( uWMrKj$y#8@!pI=OAj!bQzzMXMi9w2i2S{@=F$e%DX|RwCn3RPokOKfZ;3ozE delta 58 zcmbQucbi+_)W2Q(7#J9A8Kl`6m>3!CCO0xlZPbcqnY@biJR{5GDz*Y41||jxpb{nq KNd`_ZEd>D2p$qZ= diff --git a/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class b/target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class index 417a1c0a7ce9b24d7eeed6e3e0d8b6a5ba2267e7..2f2e735abafbb2153ffd8bb820d79e7c26663cd9 100644 GIT binary patch delta 558 zcmYjNT}u>U6n@^BaVEF19e39$Wn2nd)P-dUghE+y6fuHA*r2)zUT`% zizBh%Kj|dC$AAamKYKe~+C8kistm ziohCj%?jFD6g{@~U}sT-Q+hu=SHY0PH?8a@`vD)$#zY;f8_h0#=XQ zC6m3<>q@3`*>11P#7*2HJG@%IV`3UJln%G5Q@BS%;RE_3JT_3EM5J1Mf~T}8Qm`Ld z*b{)j3loXm6#x?yQOp@x%#a>a}uhm<1J0 zoR=^H1QUY@<1k$A>QKblMl)I%{b~f&{9M0saFhy_^@$Pg91g(_=h@=|*E)9>8H+g@ zd9cD5h>zkDwMCn%cvIGWZ55yQ-AYekYD@eiu8!e0Fp9f$BMZ_SAjfGg*2&qI-3lS{@#a0x=Y5WdW zViAerZM<)d_kG`IEly+@9y7n0c}z6WU2I+c4Nm~@<41u>AfEzSI@QLxhb#J%L@8yM zos^a`B`pZ}|52ZAcvLK5MZvOwwISFR^fd;X>wML%E%nWH8dkAJg|<9-L&GMvsNGg( zj$nr-ZN2glP81wdl|9RRiZkl4UnCz|(Wye Date: Fri, 24 Jul 2026 18:33:25 +0100 Subject: [PATCH 31/47] docs: add README and remove Spring template help file --- HELP.md | 33 ------------- README.md | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 33 deletions(-) delete mode 100644 HELP.md create mode 100644 README.md diff --git a/HELP.md b/HELP.md deleted file mode 100644 index 8604376..0000000 --- a/HELP.md +++ /dev/null @@ -1,33 +0,0 @@ -# Getting Started - -### Reference Documentation -For further reference, please consider the following sections: - -* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) -* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.5.14/maven-plugin) -* [Create an OCI image](https://docs.spring.io/spring-boot/3.5.14/maven-plugin/build-image.html) -* [Spring Boot DevTools](https://docs.spring.io/spring-boot/3.5.14/reference/using/devtools.html) -* [Spring Web](https://docs.spring.io/spring-boot/3.5.14/reference/web/servlet.html) -* [SpringDoc OpenAPI](https://springdoc.org/) -* [Spring Security](https://docs.spring.io/spring-boot/3.5.14/reference/web/spring-security.html) -* [Validation](https://docs.spring.io/spring-boot/3.5.14/reference/io/validation.html) - -### Guides -The following guides illustrate how to use some features concretely: - -* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) -* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) -* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) -* [SpringDoc OpenAPI](https://github.com/springdoc/springdoc-openapi-demos/) -* [Securing a Web Application](https://spring.io/guides/gs/securing-web/) -* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) -* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) -* [Validation](https://spring.io/guides/gs/validating-form-input/) - -### Maven Parent overrides - -Due to Maven's design, elements are inherited from the parent POM to the project POM. -While most of the inheritance is fine, it also inherits unwanted elements like `` and `` from the parent. -To prevent this, the project POM contains empty overrides for these elements. -If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides. - diff --git a/README.md b/README.md new file mode 100644 index 0000000..810ea8f --- /dev/null +++ b/README.md @@ -0,0 +1,144 @@ +# RestoReserve API + +RestoReserve API is a Spring Boot REST API for managing restaurant tables, reservations, and user access with JWT authentication. + +It started as an academic project but is being refined focused on backend fundamentals: layered architecture, validation, security, and business rules. + +## Tech Stack + +- Java 17 +- Spring Boot 3 +- Spring Web +- Spring Security +- Spring Data JPA +- H2 Database +- JWT (`jjwt`) +- Swagger / OpenAPI +- JUnit 5 + Mockito +- Maven + +## Main Features + +- User registration and login with JWT-based authentication +- Role-based access control for `ADMIN` and `USER` +- Restaurant table management with filtering by status, location, and capacity +- Reservation management with business validations +- Penalization policy for late cancellations and no-shows +- Admin endpoint to reset penalization points and reactivate users +- Swagger UI for exploring and testing the API + +## Business Rules Highlighted in the Project + +- Reservations must be created for a future date and time +- A reservation cannot exceed the maximum capacity of the selected table +- A table cannot be booked if it already has a confirmed reservation in the same 2-hour time slot +- Users with status `BANNED` cannot create new reservations +- Late cancellations increase penalization points +- Users who exceed the penalization threshold are moved to `BANNED` +- VIP tables require an additional eligibility check in the reservation flow + +## API Overview + +### Authentication + +- `POST /api/v1/auth/register` - register a new customer account +- `POST /api/v1/auth/login` - authenticate and receive a JWT token + +### Reservations + +- `POST /api/v1/reservations` - create a reservation for the authenticated user +- `GET /api/v1/reservations` - list visible reservations based on role +- `GET /api/v1/reservations/{id}` - get a reservation by id +- `GET /api/v1/reservations/status/{status}` - filter reservations by status +- `DELETE /api/v1/reservations/{id}` - cancel a reservation + +### Restaurant Tables + +- `POST /api/v1/tables` - create a table (`ADMIN`) +- `GET /api/v1/tables` - list all tables (`ADMIN`) +- `GET /api/v1/tables/{id}` - get a table by id (`ADMIN`) +- `PUT /api/v1/tables/{id}` - update a table (`ADMIN`) +- `DELETE /api/v1/tables/{id}` - delete a table (`ADMIN`) +- `GET /api/v1/tables/capacity/{pax}` - filter by minimum capacity (`ADMIN`) +- `GET /api/v1/tables/status/{status}` - filter by status (`ADMIN`) +- `GET /api/v1/tables/location/{location}` - filter by location (`ADMIN`) +- `GET /api/v1/tables/location-status?location=...&status=...` - combined filter (`ADMIN`) +- `GET /api/v1/tables/available?status=...&maxPax=...` - filter by status and capacity (`ADMIN`) + +### Users + +- `PATCH /api/v1/users/{id}/reset-penalization` - reset penalization points and restore `ACTIVE` status (`ADMIN`) + +## Validation and Error Handling + +The API uses Bean Validation on DTOs and a global exception handler to return consistent responses for: + +- validation errors +- missing resources +- business rule violations +- banned users +- non-VIP users trying to reserve VIP tables + +## Local Run + +### Requirements + +- Java 17 +- Maven Wrapper included in the project + +### Start the application + +```bash +./mvnw spring-boot:run +``` + +The application currently runs with an in-memory H2 database and seed data loaded at startup. + +## Swagger UI + +Once the app is running, open: + +```text +http://localhost:8080/swagger-ui/index.html +``` + +## Authentication Flow + +1. Register a new user with `POST /api/v1/auth/register` or use seeded data for local testing. +2. Login with `POST /api/v1/auth/login`. +3. Copy the returned JWT token. +4. In Swagger, click `Authorize` and paste `Bearer `. +5. Call protected endpoints. + +## Notes About the Current Setup + +- H2 is used here as a fast local development and testing database. +- Demo data is loaded from `src/main/resources/data.sql`. +- The project is currently optimized for local execution and portfolio presentation. +- Hardening steps such as externalized secrets and environment-specific configuration are planned as next improvements. + +## Project Structure + +```text +src/main/java/com/pachedev/restoreserve +|- controller +|- service +|- repository +|- model +|- dto +|- security +|- exception +|- config +``` + +## Future Improvements + +- Externalize JWT secret and sensitive configuration +- Add more controller, security, and integration tests +- Refactor reservation service for better transaction boundaries and testability +- Add database profiles for H2 and PostgreSQL +- Improve repository hygiene and developer experience + +## Why This Project Matters + +This project is not just a CRUD demo. It includes authentication, role-based authorization, validation, custom exception handling, and business rules around reservation conflicts and user penalization, which makes it a solid foundation for demonstrating junior backend skills with Spring Boot. From a0e57ad0566489b19565df672934bf6b17f4103d Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Fri, 24 Jul 2026 18:55:19 +0100 Subject: [PATCH 32/47] chore: ignore IDE files and remove tracked build artifacts --- .gitignore | 7 +++++ target/classes/application.properties | 13 --------- .../RestoreserveApplication.class | Bin 775 -> 0 bytes .../restoreserve/config/SwaggerConfig.class | Bin 2293 -> 0 bytes .../controller/AppUserController.class | Bin 2160 -> 0 bytes .../controller/AuthController.class | Bin 2618 -> 0 bytes .../controller/ReservationController.class | Bin 4687 -> 0 bytes .../RestaurantTableController.class | Bin 7385 -> 0 bytes .../restoreserve/dto/AuthRequestDTO.class | Bin 1915 -> 0 bytes .../restoreserve/dto/AuthResponseDTO.class | Bin 1317 -> 0 bytes .../dto/RegisterUserRequestDTO.class | Bin 2907 -> 0 bytes .../dto/ReservationRequestDTO.class | Bin 2913 -> 0 bytes .../dto/ReservationResponseDTO.class | Bin 2464 -> 0 bytes .../dto/RestaurantTableRequestDTO.class | Bin 2704 -> 0 bytes .../dto/RestaurantTableResponseDTO.class | Bin 2249 -> 0 bytes .../exception/BannedUserException.class | Bin 458 -> 0 bytes .../exception/BusinessLogicException.class | Bin 467 -> 0 bytes .../exception/ErrorResponse.class | Bin 1668 -> 0 bytes .../exception/GlobalExceptionHandler.class | Bin 5702 -> 0 bytes .../exception/NotVipUserException.class | Bin 458 -> 0 bytes .../exception/ResourceNotFoundException.class | Bin 476 -> 0 bytes .../restoreserve/model/entity/AppUser.class | Bin 4030 -> 0 bytes .../model/entity/Reservation.class | Bin 3485 -> 0 bytes .../model/entity/RestaurantTable.class | Bin 3007 -> 0 bytes .../model/enums/ReservationStatus.class | Bin 1382 -> 0 bytes .../model/enums/TableLocation.class | Bin 1238 -> 0 bytes .../model/enums/TableStatus.class | Bin 1284 -> 0 bytes .../restoreserve/model/enums/UserRole.class | Bin 1210 -> 0 bytes .../restoreserve/model/enums/UserStatus.class | Bin 1217 -> 0 bytes .../repository/AppUserRepository.class | Bin 690 -> 0 bytes .../repository/ReservationRepository.class | Bin 1214 -> 0 bytes .../RestaurantTableRepository.class | Bin 1819 -> 0 bytes .../security/CustomUserDetailsService.class | Bin 3240 -> 0 bytes .../security/JwtAuthenticationFilter.class | Bin 3633 -> 0 bytes .../restoreserve/security/JwtService.class | Bin 3167 -> 0 bytes .../security/SecurityConfig.class | Bin 6362 -> 0 bytes .../restoreserve/service/AppUserService.class | Bin 2905 -> 0 bytes .../restoreserve/service/AuthService.class | Bin 5287 -> 0 bytes .../service/ReservationService.class | Bin 13253 -> 0 bytes .../service/RestaurantTableService.class | Bin 8406 -> 0 bytes target/classes/data.sql | 25 ------------------ .../RestoreserveApplicationTests.class | Bin 573 -> 0 bytes .../service/ReservationServiceTest.class | Bin 8928 -> 0 bytes 43 files changed, 7 insertions(+), 38 deletions(-) create mode 100644 .gitignore delete mode 100644 target/classes/application.properties delete mode 100644 target/classes/com/pachedev/restoreserve/RestoreserveApplication.class delete mode 100644 target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class delete mode 100644 target/classes/com/pachedev/restoreserve/controller/AppUserController.class delete mode 100644 target/classes/com/pachedev/restoreserve/controller/AuthController.class delete mode 100644 target/classes/com/pachedev/restoreserve/controller/ReservationController.class delete mode 100644 target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/AuthRequestDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/AuthResponseDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/ReservationRequestDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.class delete mode 100644 target/classes/com/pachedev/restoreserve/exception/BannedUserException.class delete mode 100644 target/classes/com/pachedev/restoreserve/exception/BusinessLogicException.class delete mode 100644 target/classes/com/pachedev/restoreserve/exception/ErrorResponse.class delete mode 100644 target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class delete mode 100644 target/classes/com/pachedev/restoreserve/exception/NotVipUserException.class delete mode 100644 target/classes/com/pachedev/restoreserve/exception/ResourceNotFoundException.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/entity/AppUser.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/entity/Reservation.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/entity/RestaurantTable.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/enums/ReservationStatus.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/enums/TableLocation.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/enums/TableStatus.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/enums/UserRole.class delete mode 100644 target/classes/com/pachedev/restoreserve/model/enums/UserStatus.class delete mode 100644 target/classes/com/pachedev/restoreserve/repository/AppUserRepository.class delete mode 100644 target/classes/com/pachedev/restoreserve/repository/ReservationRepository.class delete mode 100644 target/classes/com/pachedev/restoreserve/repository/RestaurantTableRepository.class delete mode 100644 target/classes/com/pachedev/restoreserve/security/CustomUserDetailsService.class delete mode 100644 target/classes/com/pachedev/restoreserve/security/JwtAuthenticationFilter.class delete mode 100644 target/classes/com/pachedev/restoreserve/security/JwtService.class delete mode 100644 target/classes/com/pachedev/restoreserve/security/SecurityConfig.class delete mode 100644 target/classes/com/pachedev/restoreserve/service/AppUserService.class delete mode 100644 target/classes/com/pachedev/restoreserve/service/AuthService.class delete mode 100644 target/classes/com/pachedev/restoreserve/service/ReservationService.class delete mode 100644 target/classes/com/pachedev/restoreserve/service/RestaurantTableService.class delete mode 100644 target/classes/data.sql delete mode 100644 target/test-classes/com/pachedev/restoreserve/RestoreserveApplicationTests.class delete mode 100644 target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af5e6b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +target/ +.idea/ +*.iml +.classpath +.factorypath +.project +.settings/ \ No newline at end of file diff --git a/target/classes/application.properties b/target/classes/application.properties deleted file mode 100644 index 1dca894..0000000 --- a/target/classes/application.properties +++ /dev/null @@ -1,13 +0,0 @@ -spring.application.name=restoreserve - -spring.datasource.url=jdbc:h2:mem:testdb -spring.datasource.username=sa -spring.datasource.password= - -spring.jpa.hibernate.ddl-auto=update -spring.jpa.show-sql=true -spring.jpa.properties.hibernate.format_sql=true -spring.jpa.defer-datasource-initialization=true - -jwt.secret-key=ClaveSuperlargaYSuperSecretaNoSelaDigasANadieHazmeElFavorMiraQueGuapaEs0987654321!! -jwt.expiration=86400000 diff --git a/target/classes/com/pachedev/restoreserve/RestoreserveApplication.class b/target/classes/com/pachedev/restoreserve/RestoreserveApplication.class deleted file mode 100644 index bae8d0f4e08396804df63e938cf3fe6a0b329626..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 775 zcmb7CO>fgc5Ph2_O=<#dQz#V5fm@mbs|txrpb|vlWIoiOaNx9drp^|7H(IaL{w+=* z4*URq6k^s?iVxwy!84wZee>q+{`&pnCxDlD+(k>^G_{3Xs&qkfT1iKxw`|C*NS-%> zSIcFtQ{}ZaU9<)EF4aoOT$xP1PcA9-0^2XN(f&+eYj`pfXiw~%B5Yx&j}Ep4c4KYm zT~#FHKB^=qfkA9jmCuyZA-+kr{X&-l&*J|kHs+y)(njEL_&Ht^oO-8CHa?j}*vCN^ z1A&osnJkwff8kU?SJr)ziM3u%>ttiyKJMZkFXbu*PKFKKNv_IrylGOr2|l&P)3uiq zYc6zFxv+C%4yQvoETLS+3LI=eX5rtGU)cEv6=-{M+!#%5@x)d*{h9u9Gz X^AGsz@JnFV!clD>;t~5W_89mB)|bx} diff --git a/target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class b/target/classes/com/pachedev/restoreserve/config/SwaggerConfig.class deleted file mode 100644 index abc9adf41f30ebd80111f19c722920a0f7e43632..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2293 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 diff --git a/target/classes/com/pachedev/restoreserve/controller/ReservationController.class b/target/classes/com/pachedev/restoreserve/controller/ReservationController.class deleted file mode 100644 index 4247e5c9266fa5e48830e4a1265bfa6fe689ccb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4687 zcmcgvXp$0%c%Ggk;?k}=7k)LOgziT;W{ zZJ*XcKlBImNA>A@lQ4^B#(^#$GB>$%&pGee&;9eS-~Rw`4c{fuByctB2*fo6fuWn2 z$jQt~fs5(df|NKOs+1DZ4GOeoU1|8_aKEPxi=6LNlF!PAMXEABe`l~a?YN7&S8z>x z@qugPUr{V75ra zdAVSEX2y~uw(afpE3#s+J!uiRP&rMl&O5_Rxv-A zUN%+?*EjT4!!mP$OnpJUl0)N?BkznlIc4`o46Jv1dDDh|oKE1Bz|lB^1@$1sqA6*yT_NOjTCFn|jza$X=dC~`F^%kWgjG+x+_ zsYDwsnkqXOIG1oGfy)BtYYM7{SV^woDpbm5$Wf(Lg?qy4-#99=*_AeJIbF|GYx{_03G~+}CPAcD z`;|5+Hhy|2RJZ7xmYy~}zn(Yhs5cxmuz}cYifdB0WT{7~Iy9vrg=xmgX2~ky336{n z!}}^eo17K;LK$UrZ89e?SQ)5khvhB4AH-Y9+dlQStt8OG7F%RWwCGRF+*1`#arqi& zvl+u*+DsvuC!Id zy~^1wq}`H-e)u26{HqDjsfPt$e-f@wm76jeK1EjG_CL`kXyoOr;LK8|;??&6$Ca#M zXQjou!3jMYSEgcuC@wN%aw#%la9LPzI5etZ_EN)fJuWRyq?;qFvY4qsVp(AK%a|5a zFSaz~VRNpxonRKUc{{n7Eza?$y@*b6AMc9Eg4L-hLQ|wVJl51E`gtp)!R2k!(Hu~9qhRW*=hXFdGZ5MJ zv`L#fjai20Nq)9hj5z`w)xP7*OKo}YW$#c!E#F%Cp1@A7ZTxCN67A8h#`QHm?d&Az z;L9#PoqUR9(aiPuI=VKnPhbOw1*ZCb!O{Lx&vAVEXY{S(?CA|$Y{HK`*n}beb_A09 zxSrxw;tcQJKwroR!??~bvcwIH5K#yAVH9Je+)oVSm>}&<{?B=QFBgU87M~Wu^Gcf( zCV3*%;JuOtxA~s}e1Rtf*LDT*InwL6Q--As09*{?V_^{Qjz>>oa`KZ?%7kb@iLpHWavi+uHUw$sdIB diff --git a/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class b/target/classes/com/pachedev/restoreserve/controller/RestaurantTableController.class deleted file mode 100644 index 4278fa95bd5937da312c20ccccafa77db54e7061..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7385 zcmd5=TYD2#6kVsK4JlLFA{Or`f|de~3W(BDDW#N33bm~j#S4>UXopT_!er7?E8g$o z{r=_;@bg1IMf5@6{TKcbKi8R=OcF9FNm|i|$y{>I-fQo*_dffaKmY#icK`?QO$d57(wMO{H}6p8 zx7R^sf4x@uc%@kbgnRQup2#!$w2?E&(x7G8uI3svNnmfJ*$aA7O&V5O)xxprxSeyy zv}~6Cq0BAiGRHOPGFNxXgx``ltIcVStEuyvX{0@!>a={MgxWO+EFH4b()nwK?6e+- zzy<{^Xl=trbjNU`z{8D3kb)RC3FKG8WOdz&T()=r1{PW#uMVWl{n6cNK$7>a(EoEo=6G@O;cb;Pd$t+);_x5*P3??Q%x8-cSVd1cs?6ZFodH_ zPNMMgJrvHAYRzZ4;E)`*!lMnRSv{_~^dG2QkxvC`&w}xMm<2L;k1H6(34u3P)8>Yw zp4_K(lg)_0SOuV(DGcJ(>t3gAb}X`!+E;UeANCLw`X%Y4DFx$rLg0oNlF|l7IL7 zpHVOiOJKM$TPpb3y-eY+=5vAde&EXnQ0|$$R=9d?s*qEdR~LKV*JW}2DpsApjj9>= zM_npBc^u{yWHB!=(x^ohEUqocms`k2h!EIRPU)bPF68BBjlrx3M+3?H*Z&XIsz*>~ z7(u)I5>QV&9h?w&yArQ8d|AOFUJ-cVI(n`mQg8Ib%qN^dR#-dF)debumxxuRp5^wB z>#cLu1EfvmmfkSs{#!22Zz*^i?{IOBbn=LIAm~iZYS!?%yvB{q$yIYe1<$RSpebtY zH!Q;)@D3oVSEg$mXKtSsxHZ^?k6QB)XLcJBrkzRJbLxn0=?*tJX~ujrK2q==KIUOU zj}*~1Y3H4ke#9`@!Q8>aozi31;z8|w(i)zf)!f)hNyX`Gyh-pW-f_0XFle2!r)pW}RMLL0w; zZxqaXyBxtiFxK;-gI6c7lYEy#x_f^@*N)4$VeCiTbOm?p{1sc9aEbpkp_jjHyn|11 z?8FW}-RaNb+b+IwKJJ4`MRBUR9}jT!gZ#>4%5L7I!ri=D1V>50d$5;~eUJ}%AlLKj zK3)%>g2>?!4{S{E!+uVXKo3%+grEdK`>tT12v6Dwij~m1{c#~Q1%o)m{{sYr93r@J zK1>UI1jC`5_VGWdY%fPhf$OBQ%QzCDtKZY5_`?ESg4@8yLhm074NGuTsbO7B{2&c0 z=i(F*t>M=W&%~XVF?M`cnP#7aVUjW8Y47!ihn98(mP&dh9Wnx1JUW^~I%rmbj(8Kt zBcNowgp$WY!cA9*xGRdA=W#lW4lRUI zjE>~Bqa#)3H$OU_DnX!x2o#*b46O{J;|%Q)Y`cvR!Py7|bJs>-Mj+6tAlL%Ch+vC{ zAd7P$0w)4N?%D|42n0qI1PeG{M6lo?Si}n_{5v~sb}*B3i*uhpL393S-!xRe3#$1@i679);Lb_{&j2oAAAjxJ^%m! diff --git a/target/classes/com/pachedev/restoreserve/dto/AuthRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/AuthRequestDTO.class deleted file mode 100644 index ffff8f9d45cf23031a97efe0f9d9f57989647933..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1915 zcmb7E+inwA6kW#&8OI&tB$&{og}6Y7?F6Q2DZLnINodtbDO4fK13VquNAZyH9Ct3R z`cFK7RD_g=en3B}>N@t=9>*q99_DiP?6vl7?fLh=(|-Uw!cP`50$UB;b~>`rQccx$ z0u@G@Hx+c1(~Pw9B#v4K>W`TAr-#oi%nB^LmR;$1(m!?%R6_?%fox2KFWX9BX@B(j zMHIOHaZO<5Aoe4-t&ZH#t$XT;@9RiLuJ%KL3IQiFh@{h%p4;r1od!Qefpq;Sbe`#G zhtW>RW3^Qn%43GP^|M#*H|`|LTm#&UR!N z{@E*Gm4t)S1W&PSvK&-BENgaGH`P1@7HwR@oIqjU_0_YuU01=OWTB=Vx*@$IW@pYl zqXAsCVH-d;(tWlij^tAt%UBWEDeqroD7C4%Wm__AnfwH1%9TCCf8E9^ZU}5&lz&fP zjvA02$91)w>csQU{WLxXeCYaa^jKgc9WZHq3X)hVM*=s; zr~SJOsK`hKDY@k^gR+g=sPx)G(86yjYU$?CnI-04=s0MoUtBZ5E8`n*&&YtN9j&8~ z3hngaVGa(qEqp1kHWEtESH073Q{rjkD|{_5Umq|Cyi1qp`dxjZoWA$3(rKqK;}Dr^L9hg*<_?0Qst+K}|_)g(ze`k20wi5KQ@FN+6M{$TTKgXJl( zJG_bl_MY#npchqz7P!Cbqu!iTriDiWH_msZK>9u5OaCu~ZSK_!b(q1D*+xDso<5;9 z%WFPFjt3wQaRnpOl3Bz_-~6O+o_>PNml@?_%*7OQfx*^^5PV*${*8sVd@;$PM`Z3y zgci`no<=)NVnmzAS=!=RT5CwV$p6V@k{ied1DQ;$>M5=PXF(KtlFK505}!>0eGa_o zxr_=(gVs<=KxIZUMP;j-r}*MLl;9$+V-|Plea)md9Mtf&U#~+#GulG+12(Fg@3Bzb zdXM6}kLF_ry^72-9|O*1R-e4*QRRpE42KDJ9!z1A_YAghmrv8O`?LvsgKzO2zNan6 c(+_xv$5_FSJm06c5t*3ONv@};DIfCx129#qB>(^b diff --git a/target/classes/com/pachedev/restoreserve/dto/AuthResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/AuthResponseDTO.class deleted file mode 100644 index cbd09894acb4a96110d6bbed8cf34a979d6f8965..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1317 zcmb7DT~8B16g>lNx2=l>1QfsU1EgJq74)rYq7WlV6$69^eVT4>=t8?wXQxg4Pd-SZ zG2y`<;Eyui{gAGRB)rVrxpU7wbMC$K^VjhY0MGEm!id1Kr@C%WdTnaaz||BQ#S7^H zxh)u(1*WRC zv)s9L)uIvv$~N*S2viz=KyUlqCh1+->`;_Wd9t%7wI7{RXVIu6^(4!v*cijOz1V6j0mP|Oki4I?ZO0`0tNcmmz_{xx|*rLR`Y;7Q?I>`^3U3s z!F7R+i{?KoAu~Sp1K&J9#hA9DwpYd|Zriwlxme*IH}egdwrahTT6W2h=Es(IR9}1a z%8yzwb!q}uq6RbUrBWs|TJ{orSjGYtEj$pI8;*eP2gdJ`n>o zBlQyEb-tqXg^dwh;PZ{L(VA^-pY diff --git a/target/classes/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/RegisterUserRequestDTO.class deleted file mode 100644 index c41a51f61ed1215eb14ed8417d4d0d08f6699c05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2907 zcmbtV+fv(B6kP{=!x1rH>_EB1B{wi3Zrb#cI&H%(4XFbup**zH2VtLrf-Sj{OsBul zzvyFU`cP)dbcTn%@YavYbREf(g>C2zeUN>$&)#dFz0TfC|GYi@8^CAyI*9>+M^&p) zYUNnwG1(hGjYe3k*2aZC5WH>2;&# znM+k}xwdYYu2b5y+!bazBu=s+9Y^11nu+y#!L%BCwk$}eVC~h7eVwN_EPl^_S!g*eod`XEL$0jL=7D80?-V2WdiL87mf7nWmkr&h3*7y$n%0#c z3z{RG7gJ!VwpD9Yjhe;6)ESP>ekC6)=tk(&U23YSJI+rY{WBH4K-Nf_BhQZY^F`;L^gIiLF{SIRsvjhBy)enTlb`%~oSi+B-V$O5Lbc)$6;YM!kDR6)>YgLqe@? zYPjyN-qkSTuWo4=S8yrU@^g)`@9t{2je7zw<||j}ymH?PQU|(opsElUoL|^bc@{Oy zAK`6l?~Saw<%Mrmn)fs;;i14+S4`6nmx-Skrr~}r@O?CK z`@$k2|I4zl%icRbC$Dsy1I%%cOlKN{_(;S1_}C+tK_f4vdtlW2P&CZlTOt(n$(eK$ZDNK7B#e)|1JXCC;Pc>ZTC$k zfhQ@fQ6QB5lmj6uhXPaf#Go9Eb%sL6XPVC}p8}sb0d2!HrR^O{I!ROn?sUlObCQ6E zBsPh2xhwn6CyfhV+3}DgKFD^DV@yc|29V_2HCj`Ah8(2%0t`cB(J_rFb?EgS?)4oe zF7>0=H`D8zp)VDt?K{r@QU45MLFTL%G=TvgCfMtoE6aH5Qg5zYZ>}7axW$oY^%qq< z9;x^SNw~uZp|~pkiHSeB7{ELHO?bv6raTMyruBmj$?3ojqZ@X*8#Wbzz1|Nt4BZ?h8>E)j)bt6QcnDU@=mI3PpU$9s&pjPc$g~EVd3p`U~@gNbKS7lLfBD$ck*Oi zae0q-qOK@eJmqzvJBSk2zNArpL>Br46@lM9m(I;bgO>219W>8O1N2N47f(^TEJ{#f zKR^tR=)D|gcsN<%lH(OvGeiw|4s6-Zw$IE{Iwc5KDE@^Hi;E|iDBeH8baCkfv&G5S z2{$I=Czw0FuoN}aFObOdFE~z~#I@JMd6w2*XY$mC;6!TOwrBIPaGk>^1j@xZP+<3i zBM*);IF4ZDkNG=*Pf_+P0xKLjfmN*I8NS4GZ15F_!}4NhfPbhED4g>G@3tXvnaZdWa{!D$bs%`o3fdH8Fqmv$o9sZY1b zJ&KtR@>Y2o6}!eQWj>@gVqPGcV^KTdiZVp@QgVjn>vpo$_S%jLw_M?!D0NX4I*G0e zskdcBbF5q`=D2J(tHRkkex>v$xpFae%hD4K;i!P5hx~BS6ppPjq;nP0VcVAADqOa(63aWRisXh} z7o%YKSVIcK3=EneJ5&OY2_!y@|A{1c+9YUGnPSN ziT9}~P463q{Wz~*1_5BeNyZ{YM;9|iXSH%f&Dq=PZ@I&p!V^wx%w#ZxXBxf+wZk1yD4+|w`34vpA^=qv4^O}lgO%pg9Os_c8jbdbd9>m z&ylM7i4s&9q;QA!a#{YD@HYi#gOWHRl0mRTK1~aPrB4ks>Mk2Sf*X}Lkj(nu?}R4 zX8URgSti*SNtG^MU=8R8u^7m}X9Ga%z;C|PUGd|fNBANH6^JQ8o+HK83w(JMiqTMe zLKR&i?^2TBol+iDsU2H?^}*NV36ZM*{JG*^cv@UN$9!?^91F$!=U6J{ljkVBzchW- zIU7jL(j!V?mc)v>MXjScnWe3T_MBSxp69m|VJhk8p{XTZdAgGLj_wIO#|wI9X{Tks wez#G<4tBAJw`9+d{d*kX2f`fDyCj@ybvCH8RbK}mF7IB|yMfO&&FQ!1e`4N#&Hw-a diff --git a/target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/ReservationResponseDTO.class deleted file mode 100644 index 3468968f817078e2472ebf210e676dc45f170bd2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2464 zcmbVOT~pge6g_J`WE+8r2`wh2v@|rfAaUBJAF0x$0h+{#18H!1XrF|=1tqrRN-{J3 zPkm@+>gfy*{Q><^ot~9!SUfThJnV;d@40vP?%A{Y_rKGB0DOm^3}giEHT_Pt>okvL zOP*8%8Ad)U8Jx&!EAp$abT*fTIGF#pCmajI>{JEB$74_X2lm(_gz ze4_4qN49|3?9p#W2D?s23KTVq{U~r_n_T1^wq;_FZ8?!_xXQMa*hc)Q*8Qf_-Wg2M zZLS$eezhJ)0&`xkb0~woU!V54hfrWa3sUnUc_afuro+gIdYryrAHVUA-;(XBq{mQC zzE4;J^u7^*tE8e!{@Y&H9+%iSO#@)#_paQLVKZ>MR7V}M-FCuI9~d0hFPhtw=obh+ zpju%X53_h8u;sRFT`%o3VYG*`XrBq6eNp!8MBWONKb9qyWtJ6|RhBiDb*gdA^JEY& zU4{l;aqv9BDXSgk%D`)C%;lz}b&^5JiBjA^T_gRlZmPR2t4U$WR zERRC+8YGVoQlhMkLS{6GnF?7Qg_s&-79XcXSsR7SYLEhMra;z5A#-PtYZUEa#%?jI zD#?a*iZUSx&&Q?)p z)A}1?MK9qo0loUi9w&a4>Zm|`jT z*n$r!_=JLwB>2DqldxH5@PL0#z!G=7Lpy;V@D$Hb!*jggqF!Q`vw4p1;YaMFga*Gi V=`~?~!uy2v3Fi~WCw!0D{y$T>Gw1*S diff --git a/target/classes/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.class b/target/classes/com/pachedev/restoreserve/dto/RestaurantTableRequestDTO.class deleted file mode 100644 index 70806e964946e83ef42cce849eb9be290a5af5e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2704 zcmbtVT~pgu5Ixs67>Fq!0TO5lDkY6=LIh1x+M0gwm4?)q3@|*jlZT6a2NYz9N-{(H zZ~ETOG}AJj;h`_Q^+$Djt}WTJ%SR{mj4f%kd(Q6Jz3adJKK~QI=h#XkA+T6>tLCvR zAF7HvF+Jr6E`OAFqRdL*n%gwUnkSuLSMJ+tTYX=nWo?(iDS^S)@Z`Yi_s;WK9w+OcEIBp;V%XNH#90QJ|7fAC&+E%4*Hp_e! zc+zqL-z>So3Ym_GlddY?mj|TD+R|`pRprUDb@7{HRFpvrUrR&zhP!WD2a*L@Qed*3 zeD{ar_5`}C^7N%V6}TNseZvXVf%3?i{SWz-YPL<+d8SaNVyvQKR4;xywW?BQMx~YG zOPiT>CO2~v`ljU&xr^G=R2SQ)KY}uEUi{)%Ro7T(q6_rcZn>_Xz|-Q@?X9{MWt)of z`DVSj#W0LSkJsbsxduDnxs_Vk^0dA`x{frmJIlqblHnS=>)YGQtLr&|k*w}wG!f0V zr&h_-3xRht=t8%^K+$qksaD-rUY%Hv3%Tt*$xd-FLgQ!~T+|8;WMq&*KZlndZ&RSg z_#lH(j8Uz?ZDcx3Nk7bB1`h-_v)ABWdoBu>O3-sC{X;E-Kr)-#&{VS-Wbsg7>3>jd z2z2vAN}C60R8MH6{nq|#RSpWdZ#C0=26I>tD7`(?WzghavK%Y;Lf}=LJM4QbBx>C? znqEDOxjljD_Ots^dR#Jr@?smL4@rES!ADrEOU$6bO%)uvm8LT_>f3Q^URgb}Y&PQF zmAmbc7Louft{dq6bKF4k`?xi~OoIhawWRhMJI!oJ9(rc*6_y2h_ag*>H*t+D=fpiy zX2birbShj5Gey@usvQ?%!u?yyFi>x=HRYE*>zL~;94nCAEJVv?KwyI3DSoE~G8>MgygIq^(|93pza`Nnd{MP&6xoUET{&nC zX?WB4o52DPaFU~)#IRl|NFdFpTeSA@+v}r`4?sV}ZM00odTWHfm`cqhi%G^NSXg}co4svJjRIgT^!`-~90cIAJ^#IL+b z;646!*NwL@S+{Wivz&V&?9JtnBr1ou?d5cmPIZ!IVx*&ZKUPb#%uw$Z%^h^YPItog zH(`gkcki+y9rA$n>hjb1b4r2EApVty0t|73<}L}!13%YYTB^l^=I|&C%91HT&z}73 zIn3*#1UJiLr0|5^^GSv`=j_Tc5|Q2wA)b&a#psdzA9$LdJ;Ox);Tfj#b7z=-b7i(# z)kUPnc!?PKpA{hw==-)-j9BcI!hXV$&TZQ$JCS7dIRkePH4UQI`JzW=3lo diff --git a/target/classes/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.class b/target/classes/com/pachedev/restoreserve/dto/RestaurantTableResponseDTO.class deleted file mode 100644 index ce2377da9a61da3e77dab6e3e825299357009e1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2249 zcmbtUT~pge6g_LOWgAhz0a~{-&@?o*NTQ@^nzRb}f+lh6giK+0Xr6?<14?Acm1HLU zH~j&9XlCl^3=jPQ{ZXABeHa$Uc!tTt-7D?hb3e}Bz5o7q^bde_JlBvDxZCnOX4h^V z$hJH*0~tm>7a1H%vmN>7&)l$kf$c>*_MR*G+x5LrZtiSpm=u_OZ6DgEYkT{&Z23W3 zAm?zIZJr!7eQ)0qnDp$96sRaC+fm>oXN8Xa_NDz+;DU16@FKY{140YTA?k$!x0+{X z+wt4dHKo_2==v=?a(qwV_IVsMGZ31!wni}Ohk@$?TOSZ=ly3&V=Xg$Z zU*LB&c+y)6AfrLq8T7*#n$LO*=YbsGczw5w34E=?#I58CcKN=a%jm#wzqA8B>PQBp zu)OW}f|h*ZxV)(4v**V3crwt#W8aU$D6qS!tFVmkU}^ZC;FH-A^t{OFNHe{X%u>fa ztP7O(1|9_7sT4Ziq5npjsr574Yr8VM-1Pl7y{@J3_&&24aObzK+>~J}aJn3ZDrCd8 z!%*GGe7sWJj);D$ue0u&Dd27q4+SndZ7Uv)RyqT%bk9!$kZ`pj?-{Z%N5oem#-N|xC9J?gaTT{S6LhtbOrch zh&2^tU@ZgH7%4}~qOo>_tK(3D&-n&zXKDSAI=<8u<8RzF*7}$;uJ*BLTjn0p+W}`OaUPZ-QDEW8Z~z1xD|hk35kIpz>h*) zpuhkVwsd#z`RU!AU*Dcz0GwbwL_lb#HjgJN9dX99*m2>l1a>oy`61 zgg*b`LR1Lb_iCo%u`W@^07`TLzvJJv?~E@5Lg`O)>f(`MM&>k}$xHsc5jSgNCn z8ljPB!&lRM!0uKJ#!P4>HdW)ka=OHGvf@X&ARHzC9KAybb1n)s{OIs&HbV1~{m5q5 z%Bh?^JLwkn?9`=vq06zh{`io?(g!;rS*ScDSGGccP*7KLAi1`G@Px%jsRBe2qfb_p eA(jPi&T$+^+{js7r2T_2p>b{!4zPrk zHkxP=I*|}vSNVkPotjLU(2Gp0rekGwjTg;^%XCQ?ME_UcC-}K|Qt1cDFL{LS6}!wN zH_ED<9a|v`My9ecU+P-f?%&^XT(80&NNm-Ioj>+Y8*rnjo3SIy$ncD|M3u)QdMHdg#3at}S^s diff --git a/target/classes/com/pachedev/restoreserve/exception/ErrorResponse.class b/target/classes/com/pachedev/restoreserve/exception/ErrorResponse.class deleted file mode 100644 index 7b4f2653cf5cc981ea62e95a20090e154893271f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1668 zcmbVNTT|0O6#lj?O!R%%mZ`n8ZOh&iPTMjap&-GK+v8oXHMzN~Z3^A8>kMfjwN|aVV8~bd=Uc9A zn7b7Qr6nAP@6vJFHE8YfR-0keSCta2YU#YW#$B;ZJ3hPw+Fj5S~?p*wK!x|CR#4byN}7@mh&gVcv0CnOQUFYROy#~l^7G0i|h*`Zo{Bix2nf6r~+ z60Wexx6GE+v32pv@N#{kJm+QQ5$J_wxsGe|wvTo)n1iNZo?)V2n|8CM!9G%KVrfDDZnXg5VO zqj{kG9qKn)#Bhq$U6B@&W54N*dn6mkE}GJG16I))6I%8I;GU(Enu=E3_|Ae*NtFx%D#p2ix9*XaE2J diff --git a/target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class b/target/classes/com/pachedev/restoreserve/exception/GlobalExceptionHandler.class deleted file mode 100644 index 625a6350c80e7e379315eda31dac203bfa9a1417..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5702 zcmcgwX?Gh}8Gf$gcw}W7FQf?}O>5FLwG$Jil+Y$h+*-1n7-Ts}w!joHBk5Y6WIUtH zjGP!~SzGpfEfiYz9a=t+ra6Znz~MJ>c-nN#S1jpj7!(-2 zY-||`%do47rP5_t_63H{nYQWA2<)1iDhdpyor;WO7lw80#*n~B*0g1=zFCs)vQe_6 zz^x%zOytlEZOce%-z@7co9OqOw7 z$88}_&JgXve4W5IWzqCZ`eD|#9p6v^KsTHX-SstDN|a2yk}%qH6KzX1!-R@=Zy8pd zCV1*DO|RjAz++M3p=Wb39Hc99OUrBXODnm#I1b^kh6e=BM^V)`5_CL>hXmqP=`Tvp zGpgL-zR9Upxe7Vdg9Y^s96UKI~2^?za*g21? z_Ty2NN0S0iPwrf*-$;n6siKDC0*^+F=3z|Zq=xqhJlt2}B9oxNkg0n*4k97&NNCgq zxzd@-O-VTqNYSv&N{|u)=42?Yc1kpq%(~V3rnG(5kwCbe9k*3SUb$CmoofV8-8Zeo zqETx;2IajO%sV4&CMX^=cV`U^=LE)r^avKeXm}e05JwWjaXf`t4NtQfgoR#Ii|a@s zO;<8sQ>B2&KEtAhL<>N3_$G;qr?uP4E4balDsrp(hy_B2JE~J;+ogqaA zBr7Xq&H|N)MsADTnJ-_w2g^irNN)pGRPs!u#@jJk#{w}h5yw)qxm;#$ZKaUOw_I3P zF5F_pi9*^!G%NpI*Rieqw{InPDZi9vn9QcKnYB!Ad0}}~KyM!wie@dcA608NYs&SV zUEEIjiRj0>TIT=j$1rFye3k}Pq`SCqDR5*QpI0vYLPQsaC{-@}l8)z<3y0^I@~MTn zxlE4fXc?QOigEN*rx&za;42ZRwy3+A2 z9p6@iV;r25>jqgy^N6qq-^KSed{5xMPETg9(eVTPkSBmzolW)0kcQU9yPCAP`=rqv z5)kVGCvIcUZ0CjGpgV7>llYYE^QeY2Bd22}E zKi#%!N;Z@?&Y#}Vtm9m%*OFZZ+(~771ee+7yQfEg*;w+eWxL(h)SNaft6=&vIWmNo z1n%$NQp?%JQT#d%f#2zP0bl0C-v)dza+or@NAU-yh zS3=BqoMxKtLV382QkhZqH=_-wjj0xb>1qfjmzexsOxHp%ohGIy`eM4;AJeDs={Ba% zgkX9$1k-cG^toP4Ukt%COHAp$n4ax}X*90VpD$JZoMC9F$Q?P(CBMSoH*exQyYS{r zng~{DWYP$JgdbCU5I>>d0A36>r84fPeEu1KYTmD?_&4}H{)j(uWSsNO diff --git a/target/classes/com/pachedev/restoreserve/exception/NotVipUserException.class b/target/classes/com/pachedev/restoreserve/exception/NotVipUserException.class deleted file mode 100644 index 2ab614a6be10c7956babc71c56f444894598261a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmbVIO-sW-5PcKVMq{FbJ;K(0{G%HU`X)K&6qV#&jGwv7Zi0!Q!&6v=RO`>K)Wp#xYr0=FWC!ECp9KA~jQqFTV`RMR#HbU!)-PDY) zl~pM_w$d#en8GG}sjIPe{`ipN$_INOS*SWBSGIzOKu}MzC%Jxj^n~R{sXT-d!%tR} e0agWXFK`2Ef<%vXG{vbUb&Uj6#v2knwBG@hg>`%Y diff --git a/target/classes/com/pachedev/restoreserve/exception/ResourceNotFoundException.class b/target/classes/com/pachedev/restoreserve/exception/ResourceNotFoundException.class deleted file mode 100644 index d1da5fa15898cd44a9a5fc9c329e472982f2bbab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 476 zcmbtQO;5r=5Pe&LYDGbe9z2+M^CPi80KtQiQzZrr_obbbHSI3hZV7+OlZgj^fIrGO zMbv}w zN4TDiFX42A*0Sru)P&_3-l4YAZoj~EA7vF9-Pb$1X6jZ$+uYk1HBX_8QOA7gc%ful zje81BZTr~Y5>>-B_Dr#2S+=KphHbeDU4r~TcRXEd3&%BFPgpgf736L#Rd%3(X$QGBwtQ7KklDwMVKmQZLcW^~zejKJ)fuII1Xrr)yo zT+2629q-cvPm)5trmz}blShjR4S2#7?WS!7%M%LaL`yeJ$b8Fq?FX|8AU^yV+AzS6 zyWw+xTX)@`Z3q6$I<^T5Z@-T!)>g(3Ss`N|*JP zE8E45b(pW$Jfnjx<1v>#wsE4)P8n+pOE-;Qg79wHhUFo~acR*)sDuOQcwD;MmfBty zso`VO;yGTp9P9yeDDYu~$TfslgbT+Cb1`FOQclaHELxo|X*pIh?be=spdF7gBDl}3uEV1FY_JmX6OyESaYr0w09fQYmGV3)ByiuAE#>)&R_TddSf()HF z@eVRa*_O{$8m0>hsgPEpoa2RiDoyZ0KNi+F_jvMwN|QV}gh@^nPB)%kQE4ig-;%k< z^G{To=6N=R(+Y8cA?Hj*27Rg0484oG0BA*~Q2xX_MM=c!l1kU;1BLQXZ*|G2lRKlF z;+D9T@3ER zhA?jV_!b#9-lRft6fSOJGDrvw9;48NqbMLDHw?qA!g-^>rw)J}d4Do)S>${cJM1zE-5MDp_R1 zCqWO}e5KH2VJBYb(K{(LShjtqCY~Ccz^c5(78s~d_b$H{TzII!>8_nIC~U0exZk++ zK5~Wo{vc(zF<{h((M62<@ynSsC}@aOOrTcfavr}U*f#89a&!msu_BE_f~q|$nK+W< zAwdnFl}v_`cXsF!Bq(Poxf~pn<%2HAB=R(rx73XWI+*C9<^zK=T(fd@8q6zreoxR< zyzmHy5Y9wGljD&Pw=~dkA`Ojzq$F} zN&6KK8Tu68EFrja5#MSkpj&hsBZcnJX9=LY=|Ep3f|k;O?k9ql(}5l)f*z#WJ+&S&4!{u_$<=mIPv+~HZ=Qv84X_u0RN zx|YZMz{MZ)y3A_34c4+451ioKI+mMu;J7?XX@l~w?XGRLY~HkvPG7TnU{K0wV87fq zlwIE3FzD)W2#hwXI=*w-V%wa%fdujmnuGfr+Y4-~!#v;d1IFvjDm4&xGuE`vIP>g) zHL7+iWN;hv1F>$NCqy9e%6Ugdly>(HE2YY}2Bkw>)u20N%_?knIxi4`V;5tyJFxkA z#XaKUnCr0~&s>M^xUI0w;gaTdn;Dc9S=2=J#(3Cj*(lNoC?bP0fmj&yP&Yx)6xg9> z^PnOkMSUcMM9)Rc%G&RuW}pT+f=0oPRd(xkOKexLLf}Gd{ujCD2f%6#i8fyM7+xCN z5W{NO^_te1bILr9Jn<+2kkiyjodaVL!$=e z$_{6TvT%}n93?5O+MXk{pf=ONnS=V=*Qn2M4E-Bk0NT%q(!=J&^V$ZIT^H}b-j2k3e1v8OY}YjQUc6UaD{+~t9!+_rAk1KAzo3mp$#z1kN}zj(0`a&P}hhdM#PwixgZ2IU^z^~ z>|h~!Q$fUz!?{=eO`Xi+i!Y!?Hy!Q-j|>_qE>|aLguXNB8^V`k;=~QTI(y=X@thyH z-EA>Z|8IKk$&V@es1Q3hWVK;6y}pipx&aZ0?*YEoqSLX;Vl|1?7@k5R6O1mAi4C-t zGQWdW4(2?blRiz+^h*+ZXq9-6cY7Y_2j!8+b3TeM^sHzKWzE6z9!{9Y8Yf)C653bB zOEOd%ZomKmD?r0HU)oK!XMG|B=|L5_^4AA{EUSEijI@uw{+p7HdpX-68X8)qu>`Me3iaVf`6(DAI&QTza-%?^&zlfVfN?J@APsQy$~~(QuMu4 U?+%af`^Dh7o@lydgfrYx)G>@$Mp=`*u z>B}JW_$B?eG@D*SI;M0(J3KbG8L?Wv<%U&j-;o9M3Jkxn+Lq~9?t!_v|3cP7ft=mo z`^rh6;<*P)0++X2>1!*c1g^dj8tanZscx4y)_57mtLp*@+&j;y zDsh_H)DCL)(dp|y@jORbT01+~YpZbIfeg!Z&1h-1L#Nm`)3I;LZo7+fMk|ij-1lCZ z|2cx&_O8&k1Ju7{pbvS0ixu0I8?ENP^tEuR);y(CvwT~XD}LD<9@?zI{S3G5))Cyf zz?*DnGmW3ROKc}~rjHF2aY0~!(Dm3J6Q{t5K@|E^1LK&8&{4Tr4WiHQ62*4kz!WYC z3~}$IOahDN<4Y2k6ypN}Gq_5#s1ioSK7E;_+zR}Kfor(VdIwRx$4Y0*JzL}KSeN0U z*VwXr3K?=9P*x^HvQBiS#Hy;K0U{DLCp$5Tmon2r#OR3nGl~8(_CnQX z0dCL+YB=VRQ?1EWfvXX&`uS8Q8I%};!58e3I6T4UMWz)4z8o$SKDvuBLf9HHWLQI( z6ksxm(&}+^n%K+y{f@*mu8>OurxG9?{8r#h0;GFmV8?zpqG*wkckv#nZ|eIAywB$c z{FsagKKtf=!>wQVBZpajtILB|nB%t*0dN~1awYH)?xYZz*@!;D-4vqvY()34kV3SW zjp#Fco7QXOjeb5Webi4e{kW$866G}d#jNyEL&o$|n*J+% zokqWul|E|Yn0`jnSFoN&znqmmYW|r1nx^L{rUTA8XPNI*YZvE!$JSiWAJ~44j$JPB lMBtR_Wv?52p7eh)Hc}4XYr8$y!%9Kq4bVn zO&PFoh9Q_Mmv##K#r(GLw@SIZqICUoaZkw~2!A+VIxNO3TgqWxWmtdzAIAljJx*A$TxOez_RPQsTmC6B6m)#`krRfXNTJq?{8~Hi<6U0BnKZ>2=jO@VS$e5 z8^U?h;SCv!NYj03wymAltg~~5RR5^TN2k2zWD0%L`?E#kBk8yV%O4nu{b{`kCkMV? zF}7DSUSOHwb${iYp*5SCJ~}r?-L@uFO@j1x#~f0vmR;jJy11d}Ur%CDRU>1bON1{l;ex2w1(PnA^0^@9f*HRHW~J|x&J$F8B&v_1@QPc06>nU-_8TU?NC5x< diff --git a/target/classes/com/pachedev/restoreserve/model/enums/TableLocation.class b/target/classes/com/pachedev/restoreserve/model/enums/TableLocation.class deleted file mode 100644 index 4a1f31b4772f651b82b071f50fe0a97e0499a053..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1238 zcmbVL-%k@k5dN0C_S)lsS_P!w@2YJE~#@TCy^TC%kx!s+enQy-NcKYl0w@UzT@Iu2F!%N%mn8(~c6m8Kr1JMnA zT13znX2)*}*A!l_(=~VbzAI|J%|plcG^h-dN4(EXmwN|hl|mR2)<&&SXLwZ`0kX`X z?N)boHa4pa@#Eyj5(bDfXs;1d=Ie(wH%zAt_I{4Wy7JsG8%6datuDf=DKYtW?Tv z^1zX@MVC4hOc90f(CIShc`0k&;~l{;F);j~?ReZ}$ZU)7&~Ja>0l7mFP<*n_-JYPs z>q2ofIdnY2V-0zNkF|Ny4MoSm6Fg-|^B~|SwtsxWP>PGu*gq0>Sl){1^Y@AbqKAvf zH9j$H#nXlw7DswLDc7Qb0+wi?;;5T0?{>>EDpzH$+miE01Emh+)h)jl*y63@lGfRO z26u}%pOe!A&`yH`WO8zB$fMG(W1Qj) zvYKSe-|}~`R(3);$K)BZk#S3njOoM=aw(_CS}pYy)7BYgzf#5+=4hRas;QWuYB@UR zB9HXaoYN|6tkAvWq?HyI&M|+ToFo!NHA*s~vxWsp7We2&kKs-RxJzFe_wiuJc$F$i zw3$=Pw%#kH%T`4a+*al<@Nnr1c?Qe|;OvS~nx3^dWK^PEme#YZbb-=Ou>a>tDhY;X PpC#~e1aIPbw6FXDs$VCX diff --git a/target/classes/com/pachedev/restoreserve/model/enums/TableStatus.class b/target/classes/com/pachedev/restoreserve/model/enums/TableStatus.class deleted file mode 100644 index 9689e08449201edd9e291e8901bcb13f30488abc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1284 zcmbVMTTc@~7(GMpWszF6%Efz?wje8Vd1)oox|+sv2`fz+pN8!SOZGyx+a|pEle9cQ zG!mcvQO0k!752pkyUEVXH}lPz^PM@nzkYwa1n>&$35+o;8?LRMaN|g{MNjoa$9Jg_ zUQejD+ZL88oUYwbKkx%f=sx$modlu`H;#Fas}^?-RgDrDQq^X)-l)EAXbdZj{{}5H zB=FYaa+NP>&(oa1CxXxo1i>gUK`^$Pgd@$m}qr^HS}+!)?JZF$i3*Z93c{o?F5{ za@)Jyqh()slppVLt1D=*UMP;DOd9hjCQu;oP{pSmU)TytSfFd=p2trO_vDnJ6xL%5N}P3bk2mly(D)~)w+4;kk>t0)I)IU_>fU37C=S_q(? z+!OR<93V3zZ6hLV)<0RgSE0ZBIlSq!&DI5js`(42=2y$U^@PT@*;gq z>8)rVIlUF@V_HAM>{kLA!#!%V0c4cmW0;|%=ZV<;U{z+&w?M5tdYR@W%U z0pi3B!%dPWrQo5g0Z-^njp0!Scua2!c|08!vQDQ-v|Jyvt@n}AWve2oZbfD<@NDr5 wtqj-=z}Xd}6y5mqA)^xY^0;)Cl`gRK6YSsnB$XKb-#$y=mjV0=R)c=+4|bL;WB>pF diff --git a/target/classes/com/pachedev/restoreserve/model/enums/UserRole.class b/target/classes/com/pachedev/restoreserve/model/enums/UserRole.class deleted file mode 100644 index 843cd7554767544d7663c3f9824838dcbe982140..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1210 zcmb7D-A~g{6#reity}3}P67Gwd+IiDWg30iBp}X&xPhT=$>M{#bXO?pTGDk(c=JzE zcz|dmKKn-*&+P{E#fLSyALpKPe&=^S`seSD3jnY2T*U-K$@03!5w{LRM+^*K^a77I z;SYq-^*X{agxl}-j3zzpdX7*LW0*SR18z9n-8X8aWk~Jr)N7xcW^I>YwLTtVg+UA5 zH{Le3wizaC+s#IlQKynu7P1%M+P?-64jB( zY`%V-jv4s2yIQIr8x7#|BND)-scExtTyC?jQe9sXK*}7+O zr^S6+%Hg911=B)cWjqC44H-q4!q6>?vpwYKG`P*-02It zyvP^Eu7rk1c&s8v$dL?>dx7Zcc!H^FI=t7bL|9x!xXwt9APrG$SX9mQ`o1OJ*|Hz$ z%dsst&h-g?=VweO^uqKqn0ekXeu;LYf$z8Yan3 zC#glU{7OH;+S#;nhN)9zL*Z5|6lUVTNTr-0Yqn!2m@!W=_nkZ@Fi-nb7%hf0MO&cz zLa32inrYf)j557TPHJ&^=?sh4&IuwxR6{2{ysKD}WO0vHY65pMz+GA?+{c3v<5v_( zqRpOQuKivqUbL%{U_)6v$HSFxq!}_Bg0m||DQae6#HdK846S8Z@f^k9VE^}%RN@TJ PzDnRy2yb9DJj?$8o!=Za diff --git a/target/classes/com/pachedev/restoreserve/model/enums/UserStatus.class b/target/classes/com/pachedev/restoreserve/model/enums/UserStatus.class deleted file mode 100644 index cf9c9d069f95c1bd4cf464ac4c770801021377da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1217 zcmbVL-A~g{6#re?ty?KDr+|F;J#`yM0i!RQ1cZ?+j$u+*viNlAu29mor0tgQ=AWeS z0MST%_Kz~2+fB9?AE3$oIA6c>JLjJM{`36`z&osJ7-3kp{kD0+?IY0=T{942BCs zTMVO>`un^Z^&2^|a*1~Rxq8Owc_seElv zixmZqcUYC2r12RVYv_E3$dVI&H1OI?l5REv)#`O z+rwe~1{QAqZn9Ae2J%>x)efcIba@z-h7{L11~thUq%l%^jnyr`6WC(gk#90FnA{~9 zKs8&=p+}P(kacSaSAvvpPJFGjzCu%>&_AWD&&Y`Z=%ir*GP812$fDAzW0d?1k{Tq- zZ|MhEGdreSVEi1}Sh%Og!ertnnUphRt)_a0N$VWb-zZ`PGqjJ#*;I^CwpqH*#ujO% z*`{6QSRuXSq!kzDFEBRC8V4EiUBkR2i$}E5BY2Pj9@0wVF`o1pzokqPZR!lu z%@0cPs#%rKmw8mm0cqEdL_io4NTL)Gd94Zs?R2!LuLnC6mSLTif!U{gtqFD4oi)BFu&7n+! z(`nu(p)>~Vwzq81w|PN#e-33DG#jRrI^aW&Af43?A5WGFE1 z(OP`Ow2*#ncxpn1K(WobjC#y>>3O5go7#jNfnBLw8U{-E?yX`SelOJ>b(m(f9k6ua zlYos15-8jX-#O}C;Q&>Kz*gBFmA9quTHDD%q=iRq5o(J-zAjv!=}2(`*FPrg+usxJ zNQZlr`&#Hvl&uQ3$wmF4if3H6q;tVOh-t3E;n;PJV_wy|_`7P8 zlMnmU){-A|$r>K7I)}b9;C%B%`M=cQ>Hn%vtcj@;5eTfNw>G7TA#jIh>M~MIek?rP z&(c^U?R+dbfydOcCtkAbiF`B>rBw6SX%!27OkfJ1EZ!Fx^tuVtU_c%t17@Ir-zfgc5Sh0qUBLV9h-dVd>+EGZh z^|;u}oQkBUQr8mDY^zYC%Pa|aH%`t|Z}cc)6Hp{cJLUcwMr(_^T}rUegV-h4F~J@V z^fK*Kz+$1M?U)LO+Z$ta$>LPzb1t&ZQ}<~Wb*55W=M#}jS}GKJ#z&`&7O-<6Ze`wi zDnHMr(o{O%%vLXF2M^zrB#W(Y`tX#{+HWOHTlD|{ diff --git a/target/classes/com/pachedev/restoreserve/security/CustomUserDetailsService.class b/target/classes/com/pachedev/restoreserve/security/CustomUserDetailsService.class deleted file mode 100644 index 76d05aab0e8199950a8909ee68024505cfd2a323..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3240 zcmb_eS$7mw5dLm5q{Gmd5FiLh0vL1>kR~XI4vB6q3331>3 z4L4*7{E6Mngp^Xzid@x zX-e5@#tKvz`FyEhMp>aM#eoyeS-F_AbN*FGO11Y;S(ux>HR0?46RTM2_!;1~v8Xb|WX5WjRS` ziX+FhMiT3!o9$p2J#Atqb_q03sz{(a(_7;+**FnQ9(A1XV7I}552Jz z&qO!&3T*O&vu>!yrUHLfpueun3V?NS4bhi1uwUTt%9NDJqH8HH(ve*clr;9C$HW0V zNBOj5Awaf0v?vi#kB~Gn=rhnOaCGh7HE|G!1k70{np%)Ewk+vuNAF1@Ja$|=p?e<= zo3I|5bybETFQT1BKVCF&RABJGNigw}wxzXF2R3kgQ9`KkrP)1U;uX9qV36HW-{sXi zN@gGLrMMg>$w8lC_?*O`fq^ITZ{tZ5uW1fa`ete4hfJJCj=q+0H03kLIVu#mmbv-= zWP>G6IBVh@&I@d$^+UW9GqivEqR2Y!(?IFb#EZCW;1bi{LSm{fn#g0AkyEVg9hYuQ zoaRjL4Q>&aj85Me6;K(Xs zmdhDL-du=eX?j>z>I%0HD~6MORqBctsesD2oOC=V8f2Z#tb)I4>FOO9*gYCEVU^WI zZ^j87QZ(dwek7AJquF!)a?zi*&MQv^GEz3e9OOpsAz;41F^vVg=;S6R{U`PfAFATnU+_FXFH~_P^*fGTZ|30TDzb$tPVx6l z6&JpR(fmD9sVc5iG5SLyZk+F3XyLnyV!PQ=b)l20^_SQt>O!2Y*J}j~q2Y>#O$xTNB+nYrE!kOD9Cmi!jjMp4Oh`5e?gym_pd! zU|#QV?qXWg&vR>Pw**cI*9QZzQ9-PCi~f4 IeT|*}0_GyfKmY&$ diff --git a/target/classes/com/pachedev/restoreserve/security/JwtAuthenticationFilter.class b/target/classes/com/pachedev/restoreserve/security/JwtAuthenticationFilter.class deleted file mode 100644 index eee3f7b8ee204efcc8f7943221446f391e339511..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3633 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 Dg4tLj diff --git a/target/classes/com/pachedev/restoreserve/service/AuthService.class b/target/classes/com/pachedev/restoreserve/service/AuthService.class deleted file mode 100644 index 54a42a8de5d421aa732061f6b4e43971ec8d9e96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5287 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 C8b$E{ diff --git a/target/classes/com/pachedev/restoreserve/service/ReservationService.class b/target/classes/com/pachedev/restoreserve/service/ReservationService.class deleted file mode 100644 index 22209db70db3d870e1fadd3c12b72be5cc4ba1eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13253 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 ae~w?_H~150{*1qJT=trO!$0sZwEP#I0X8lG diff --git a/target/classes/data.sql b/target/classes/data.sql deleted file mode 100644 index 1ae8510..0000000 --- a/target/classes/data.sql +++ /dev/null @@ -1,25 +0,0 @@ -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 1', 4, 'AVAILABLE', 'SALON', false); - -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 2', 2, 'RESERVED', 'TERRACE', false); - -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON', false); - -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE', false); - - -- test credentials-> username: armymoves - password: 1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true, 0, 'ACTIVE'); - --- test credentials-> username: admin - password: Admin1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true, 0, 'ACTIVE'); - -INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED'); - -INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED'); \ No newline at end of file diff --git a/target/test-classes/com/pachedev/restoreserve/RestoreserveApplicationTests.class b/target/test-classes/com/pachedev/restoreserve/RestoreserveApplicationTests.class deleted file mode 100644 index 5ea6b03c5c1ab2a2b737c869d7d44d2e4cded4b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 573 zcmbVJO-}+b5Pe+`6i^Von0U~eABnvg6F(9(@wCwd2>0$%p}5;_x?S{dc{1_f5Aa7B zrzk7hD<47CfTmAhi792|}qsyAjV8>nEdg%#8o+M&|&u1F(k zhayTOLpLqS@^%{&KX*<(M~?Q(1CoLD5;(k$LY79(aV|lCmwEKUFa^m$MNlU&wvr11MtVrdPv5!15 z#&JiKd@&gxl!$ApmoH%tBpZDV5Rj2vl-KFRDKcLj)(vN diff --git a/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class b/target/test-classes/com/pachedev/restoreserve/service/ReservationServiceTest.class deleted file mode 100644 index a6bbe003f164f04162f468181ed53f801aad5e2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8928 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 Date: Thu, 24 Sep 2026 20:50:21 +0100 Subject: [PATCH 33/47] chore: add project metadata --- pom.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 42b9fe1..1d1dcdb 100644 --- a/pom.xml +++ b/pom.xml @@ -11,14 +11,16 @@ com.pachedev restoreserve 0.0.1-SNAPSHOT - - - + RestoReserve API + Spring Boot REST API for managing restaurant tables, reservations, and JWT-based authentication. + https://github.com/cloudxdam/restoreserve-api - + + Daniel Pacheco + From ba36269fc2a6ce951105936f76d906aa2156924a Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Thu, 24 Sep 2026 20:51:34 +0100 Subject: [PATCH 34/47] docs: update README --- README.md | 240 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 178 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 810ea8f..fce72ef 100644 --- a/README.md +++ b/README.md @@ -2,82 +2,118 @@ RestoReserve API is a Spring Boot REST API for managing restaurant tables, reservations, and user access with JWT authentication. -It started as an academic project but is being refined focused on backend fundamentals: layered architecture, validation, security, and business rules. +The project started as an academic project and is being refined with a focus on backend fundamentals: layered architecture, validation, security, persistence, testing, and business rules. ## Tech Stack - Java 17 -- Spring Boot 3 +- Spring Boot 3.5.14 - Spring Web - Spring Security - Spring Data JPA - H2 Database -- JWT (`jjwt`) -- Swagger / OpenAPI +- JJWT +- Spring Validation +- Springdoc OpenAPIz +- Lombok - JUnit 5 + Mockito +- Spring Security Test - Maven ## Main Features - User registration and login with JWT-based authentication +- BCrypt password hashing +- Stateless authentication with Spring Security - Role-based access control for `ADMIN` and `USER` - Restaurant table management with filtering by status, location, and capacity - Reservation management with business validations -- Penalization policy for late cancellations and no-shows +- Reservation ownership and access control +- Penalization policy for late cancellations +- Automatic user banning after exceeding the penalization threshold - Admin endpoint to reset penalization points and reactivate users -- Swagger UI for exploring and testing the API +- Global exception handling +- Swagger / OpenAPI documentation -## Business Rules Highlighted in the Project +## Architecture -- Reservations must be created for a future date and time -- A reservation cannot exceed the maximum capacity of the selected table -- A table cannot be booked if it already has a confirmed reservation in the same 2-hour time slot -- Users with status `BANNED` cannot create new reservations -- Late cancellations increase penalization points -- Users who exceed the penalization threshold are moved to `BANNED` -- VIP tables require an additional eligibility check in the reservation flow +The application follows a layered architecture: + +```text +Controller + ↓ +Service + ↓ +Repository + ↓ +Database +``` + +Controllers handle HTTP requests and validation, services contain business rules, and repositories provide persistence through Spring Data JPA. + +Security is handled separately through Spring Security and a custom JWT authentication filter. + +## Business Rules + +The reservation service implements several domain rules: + +- Reservations must be created for a future date and time. +- A reservation cannot exceed the maximum capacity of the selected table. +- A table cannot have overlapping confirmed reservations. +- Each confirmed reservation occupies a two-hour time window. +- Users with status `BANNED` cannot create new reservations. +- Cancelling a reservation less than two hours before its scheduled time adds 2 penalty points. +- Users exceeding 6 penalty points are moved to `BANNED`. +- Administrators can reset a user's penalization and restore `ACTIVE` status. ## API Overview ### Authentication -- `POST /api/v1/auth/register` - register a new customer account -- `POST /api/v1/auth/login` - authenticate and receive a JWT token +- `POST /api/v1/auth/register` — register a new user +- `POST /api/v1/auth/login` — authenticate and receive a JWT token ### Reservations -- `POST /api/v1/reservations` - create a reservation for the authenticated user -- `GET /api/v1/reservations` - list visible reservations based on role -- `GET /api/v1/reservations/{id}` - get a reservation by id -- `GET /api/v1/reservations/status/{status}` - filter reservations by status -- `DELETE /api/v1/reservations/{id}` - cancel a reservation +- `POST /api/v1/reservations` — create a reservation for the authenticated user +- `GET /api/v1/reservations` — list reservations accessible to the current user +- `GET /api/v1/reservations/{id}` — get a reservation by ID +- `GET /api/v1/reservations/status/{status}` — filter reservations by status +- `DELETE /api/v1/reservations/{id}` — cancel a reservation ### Restaurant Tables -- `POST /api/v1/tables` - create a table (`ADMIN`) -- `GET /api/v1/tables` - list all tables (`ADMIN`) -- `GET /api/v1/tables/{id}` - get a table by id (`ADMIN`) -- `PUT /api/v1/tables/{id}` - update a table (`ADMIN`) -- `DELETE /api/v1/tables/{id}` - delete a table (`ADMIN`) -- `GET /api/v1/tables/capacity/{pax}` - filter by minimum capacity (`ADMIN`) -- `GET /api/v1/tables/status/{status}` - filter by status (`ADMIN`) -- `GET /api/v1/tables/location/{location}` - filter by location (`ADMIN`) -- `GET /api/v1/tables/location-status?location=...&status=...` - combined filter (`ADMIN`) -- `GET /api/v1/tables/available?status=...&maxPax=...` - filter by status and capacity (`ADMIN`) +- `POST /api/v1/tables` — create a table (`ADMIN`) +- `GET /api/v1/tables` — list all tables (`ADMIN`) +- `GET /api/v1/tables/{id}` — get a table by ID (`ADMIN`) +- `PUT /api/v1/tables/{id}` — update a table (`ADMIN`) +- `DELETE /api/v1/tables/{id}` — delete a table (`ADMIN`) +- `GET /api/v1/tables/capacity/{pax}` — filter by minimum capacity (`ADMIN`) +- `GET /api/v1/tables/status/{status}` — filter by status (`ADMIN`) +- `GET /api/v1/tables/location/{location}` — filter by location (`ADMIN`) +- `GET /api/v1/tables/location-status?location=...&status=...` — combined filter (`ADMIN`) +- `GET /api/v1/tables/available?status=...&maxPax=...` — filter by status and capacity (`ADMIN`) ### Users -- `PATCH /api/v1/users/{id}/reset-penalization` - reset penalization points and restore `ACTIVE` status (`ADMIN`) +- `PATCH /api/v1/users/{id}/reset-penalization` — reset penalization points and restore `ACTIVE` status (`ADMIN`) ## Validation and Error Handling -The API uses Bean Validation on DTOs and a global exception handler to return consistent responses for: +The API uses Bean Validation on request DTOs and a global exception handler to provide consistent error responses for: + +- Validation errors +- Missing resources +- Business rule violations +- Banned users +- Other application-specific exceptions -- validation errors -- missing resources -- business rule violations -- banned users -- non-VIP users trying to reserve VIP tables +Custom exceptions include: + +- `ResourceNotFoundException` +- `BusinessLogicException` +- `BannedUserException` +- `NotVipUserException` ## Local Run @@ -88,57 +124,137 @@ The API uses Bean Validation on DTOs and a global exception handler to return co ### Start the application +Linux / macOS: + ```bash ./mvnw spring-boot:run ``` -The application currently runs with an in-memory H2 database and seed data loaded at startup. +Windows: + +```powershell +mvnw.cmd spring-boot:run +``` + +The application uses an in-memory H2 database with seed data loaded from: + +```text +src/main/resources/data.sql +``` ## Swagger UI -Once the app is running, open: +Once the application is running, open: ```text http://localhost:8080/swagger-ui/index.html ``` +The OpenAPI documentation includes Bearer JWT authentication, allowing protected endpoints to be tested directly from Swagger UI. + ## Authentication Flow -1. Register a new user with `POST /api/v1/auth/register` or use seeded data for local testing. +1. Register a user with `POST /api/v1/auth/register` or use the seeded users for local testing. 2. Login with `POST /api/v1/auth/login`. 3. Copy the returned JWT token. -4. In Swagger, click `Authorize` and paste `Bearer `. -5. Call protected endpoints. +4. In Swagger UI, click `Authorize`. +5. Enter `Bearer `. +6. Call the protected endpoints. -## Notes About the Current Setup +## Demo Users + +The project includes seeded users for local development. + +### User -- H2 is used here as a fast local development and testing database. -- Demo data is loaded from `src/main/resources/data.sql`. -- The project is currently optimized for local execution and portfolio presentation. -- Hardening steps such as externalized secrets and environment-specific configuration are planned as next improvements. +```text +Username: armymoves +Password: 1234 +Role: ROLE_USER +``` + +### Administrator + +```text +Username: admin +Password: Admin1234 +Role: ROLE_ADMIN +``` + +These credentials are intended for local development and demonstration only. ## Project Structure ```text src/main/java/com/pachedev/restoreserve -|- controller -|- service -|- repository -|- model -|- dto -|- security -|- exception -|- config +├── config +├── controller +├── dto +├── exception +├── model +│ ├── entity +│ └── enums +├── repository +├── security +└── service +``` + +## Testing + +The project uses: + +- JUnit 5 +- Mockito +- Spring Boot Test +- Spring Security Test + +Current tests cover application context loading and reservation-service business rules, including validation, banned users, reservation ownership, cancellation, penalization, and automatic banning. + +Run the test suite with: + +```bash +./mvnw test ``` +## Notes About the Current Setup + +- H2 is used as a lightweight local development database. +- Demo data is loaded from `data.sql`. +- JWT authentication is implemented with JJWT and Spring Security. +- The project is currently focused on local execution and portfolio presentation. +- Sensitive configuration should be externalized before production deployment. + ## Future Improvements -- Externalize JWT secret and sensitive configuration -- Add more controller, security, and integration tests -- Refactor reservation service for better transaction boundaries and testability -- Add database profiles for H2 and PostgreSQL -- Improve repository hygiene and developer experience +- Externalize JWT secrets and sensitive configuration. +- Add more controller, security, and integration tests. +- Improve transaction boundaries and testability in the reservation service. +- Add database profiles for H2 and PostgreSQL. +- Improve repository and development workflow. +- Add production-oriented configuration and observability. ## Why This Project Matters -This project is not just a CRUD demo. It includes authentication, role-based authorization, validation, custom exception handling, and business rules around reservation conflicts and user penalization, which makes it a solid foundation for demonstrating junior backend skills with Spring Boot. +This project goes beyond a basic CRUD application. + +It demonstrates practical backend concepts including: + +- REST API design +- Authentication and authorization +- JWT security +- Role-based access control +- DTO-based request and response handling +- Bean Validation +- JPA persistence +- Business-rule implementation +- Centralized exception handling +- Unit testing and mocking +- API documentation with OpenAPI + +It provides a foundation for demonstrating junior backend development skills with Java and Spring Boot while continuing to evolve toward a more production-oriented architecture. + +## Author + +**Daniel Pacheco** + +GitHub: `cloudxdam` From 5785336ac9710da8582e3da8090d6daebd645b33 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 5 Oct 2026 16:03:32 +0100 Subject: [PATCH 35/47] fix: move jwt secret to environment variable and rotate it --- .env.example | 3 +++ .gitignore | 3 ++- README.md | 16 ++++++++++++++-- src/main/resources/application.properties | 4 +++- 4 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9c3b364 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +# Required environment variables. +# See "Configure the JWT secret" in README.md to generate .env with a real value. +JWT_SECRET= diff --git a/.gitignore b/.gitignore index af5e6b6..30dcb8c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ target/ .classpath .factorypath .project -.settings/ \ No newline at end of file +.settings/ +.env diff --git a/README.md b/README.md index fce72ef..9b03df4 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,20 @@ Custom exceptions include: - Java 17 - Maven Wrapper included in the project +### Configure the JWT secret + +The application signs its tokens with a key read from the `JWT_SECRET` environment +variable. The key is not stored in the repository, so the application will not start +until you provide one. + +Create a local `.env` file (already ignored by git) with a freshly generated key: + +```bash +printf 'JWT_SECRET=%s\n' "$(openssl rand -base64 64)" > .env +``` + +The required variables are documented in `.env.example`. Never commit `.env`. + ### Start the application Linux / macOS: @@ -222,11 +236,9 @@ Run the test suite with: - Demo data is loaded from `data.sql`. - JWT authentication is implemented with JJWT and Spring Security. - The project is currently focused on local execution and portfolio presentation. -- Sensitive configuration should be externalized before production deployment. ## Future Improvements -- Externalize JWT secrets and sensitive configuration. - Add more controller, security, and integration tests. - Improve transaction boundaries and testability in the reservation service. - Add database profiles for H2 and PostgreSQL. diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1dca894..98d8cab 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,5 +1,7 @@ spring.application.name=restoreserve +spring.config.import=optional:file:.env[.properties] + spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.username=sa spring.datasource.password= @@ -9,5 +11,5 @@ spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.defer-datasource-initialization=true -jwt.secret-key=ClaveSuperlargaYSuperSecretaNoSelaDigasANadieHazmeElFavorMiraQueGuapaEs0987654321!! +jwt.secret-key=${JWT_SECRET} jwt.expiration=86400000 From 3a231eeca307409f61e282cb03bd6de0b5ec4867 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 5 Oct 2026 16:03:32 +0100 Subject: [PATCH 36/47] chore: regenerate maven wrapper --- .mvn/wrapper/maven-wrapper.properties | 3 + mvnw | 0 mvnw.cmd | 378 +++++++++++++------------- 3 files changed, 192 insertions(+), 189 deletions(-) create mode 100644 .mvn/wrapper/maven-wrapper.properties mode change 100644 => 100755 mvnw diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ffcab66 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,3 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/mvnw.cmd b/mvnw.cmd index 92450f9..5761d94 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,189 +1,189 @@ -<# : batch portion -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.4 -@REM -@REM Optional ENV vars -@REM MVNW_REPOURL - repo url base for downloading maven distribution -@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven -@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output -@REM ---------------------------------------------------------------------------- - -@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) -@SET __MVNW_CMD__= -@SET __MVNW_ERROR__= -@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% -@SET PSModulePath= -@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( - IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) -) -@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% -@SET __MVNW_PSMODULEP_SAVE= -@SET __MVNW_ARG0_NAME__= -@SET MVNW_USERNAME= -@SET MVNW_PASSWORD= -@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) -@echo Cannot start maven from wrapper >&2 && exit /b 1 -@GOTO :EOF -: end batch / begin powershell #> - -$ErrorActionPreference = "Stop" -if ($env:MVNW_VERBOSE -eq "true") { - $VerbosePreference = "Continue" -} - -# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties -$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl -if (!$distributionUrl) { - Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" -} - -switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { - "maven-mvnd-*" { - $USE_MVND = $true - $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" - $MVN_CMD = "mvnd.cmd" - break - } - default { - $USE_MVND = $false - $MVN_CMD = $script -replace '^mvnw','mvn' - break - } -} - -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ -if ($env:MVNW_REPOURL) { - $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } - $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" -} -$distributionUrlName = $distributionUrl -replace '^.*/','' -$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' - -$MAVEN_M2_PATH = "$HOME/.m2" -if ($env:MAVEN_USER_HOME) { - $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" -} - -if (-not (Test-Path -Path $MAVEN_M2_PATH)) { - New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null -} - -$MAVEN_WRAPPER_DISTS = $null -if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { - $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" -} else { - $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" -} - -$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" -$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' -$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" - -if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { - Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" - Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" - exit $? -} - -if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { - Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" -} - -# prepare tmp dir -$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile -$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" -$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null -trap { - if ($TMP_DOWNLOAD_DIR.Exists) { - try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } - catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } - } -} - -New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null - -# Download and Install Apache Maven -Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." -Write-Verbose "Downloading from: $distributionUrl" -Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" - -$webclient = New-Object System.Net.WebClient -if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { - $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) -} -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null - -# If specified, validate the SHA-256 sum of the Maven distribution zip file -$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum -if ($distributionSha256Sum) { - if ($USE_MVND) { - Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." - } - Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash - if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { - Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." - } -} - -# unzip and move -Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null - -# Find the actual extracted directory name (handles snapshots where filename != directory name) -$actualDistributionDir = "" - -# First try the expected directory name (for regular distributions) -$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" -$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" -if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { - $actualDistributionDir = $distributionUrlNameMain -} - -# If not found, search for any directory with the Maven executable (for snapshots) -if (!$actualDistributionDir) { - Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { - $testPath = Join-Path $_.FullName "bin/$MVN_CMD" - if (Test-Path -Path $testPath -PathType Leaf) { - $actualDistributionDir = $_.Name - } - } -} - -if (!$actualDistributionDir) { - Write-Error "Could not find Maven distribution directory in extracted archive" -} - -Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" -Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null -try { - Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null -} catch { - if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { - Write-Error "fail to move MAVEN_HOME" - } -} finally { - try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } - catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } -} - -Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" From a6e5d7f5c9284e78ff80fabb541ce28c24b0cce6 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 5 Oct 2026 20:10:35 +0100 Subject: [PATCH 37/47] test: add integration tests exposing missing transaction boundaries --- .../service/ReservationServiceTest.java | 10 +- .../service/ReservationServiceTxTest.java | 109 ++++++++++++++++++ 2 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java index c1c4ee3..fef5f5e 100644 --- a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java @@ -181,9 +181,9 @@ void addPenalizationPointsByLateCancellation() { reservationService.cancelReservation(reservation.getId()); - assertEquals(client.getPenalizationPoints(), 2); - assertEquals(client.getStatus(), UserStatus.ACTIVE); - assertEquals(reservation.getStatus(), ReservationStatus.CANCELLED); + assertEquals(2, client.getPenalizationPoints()); + assertEquals(UserStatus.ACTIVE, client.getStatus()); + assertEquals(ReservationStatus.CANCELLED, reservation.getStatus()); } /** @@ -230,7 +230,7 @@ void banClientByExceedingSixPenalizationPoints() { reservationService.cancelReservation(reservation.getId()); - assertEquals(client.getPenalizationPoints(), 8); - assertEquals(client.getStatus(), UserStatus.BANNED); + assertEquals(8, client.getPenalizationPoints()); + assertEquals(UserStatus.BANNED, client.getStatus()); } } diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java new file mode 100644 index 0000000..225c5b6 --- /dev/null +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java @@ -0,0 +1,109 @@ +package com.pachedev.restoreserve.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.TestConstructor; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; + +import com.pachedev.restoreserve.dto.ReservationRequestDTO; +import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.model.entity.Reservation; +import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.ReservationStatus; +import com.pachedev.restoreserve.model.enums.UserStatus; +import com.pachedev.restoreserve.repository.AppUserRepository; +import com.pachedev.restoreserve.repository.ReservationRepository; +import com.pachedev.restoreserve.repository.RestaurantTableRepository; + +@SpringBootTest(properties = "spring.datasource.url=jdbc:h2:mem:txtestdb") +@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) +class ReservationServiceTxTest { + + private static final Long SEED_USER_ID = 1L; + private static final Long SEED_RESERVATION_ID = 1L; + private static final Long SEED_TABLE_ID = 1L; + private static final Long FILLER_TABLE_ID = 3L; + + private final ReservationService reservationService; + private final AppUserRepository appUserRepository; + private final RestaurantTableRepository tableRepository; + + @MockitoSpyBean + private ReservationRepository reservationRepository; + + ReservationServiceTxTest(ReservationService reservationService, + AppUserRepository appUserRepository, + RestaurantTableRepository tableRepository) { + this.reservationService = reservationService; + this.appUserRepository = appUserRepository; + this.tableRepository = tableRepository; + } + + @BeforeEach + void seed() { + AppUser user = appUserRepository.findById(SEED_USER_ID).orElseThrow(); + user.setPenalizationPoints(0); + user.setStatus(UserStatus.ACTIVE); + appUserRepository.save(user); + + Reservation reservation = reservationRepository.findById(SEED_RESERVATION_ID).orElseThrow(); + reservation.setStatus(ReservationStatus.CONFIRMED); + reservationRepository.save(reservation); + + RestaurantTable table = tableRepository.findById(SEED_TABLE_ID).orElseThrow(); + table.setIsVip(false); + tableRepository.save(table); + + if (reservationRepository.findByStatus(ReservationStatus.CONFIRMED).size() <= 3) { + RestaurantTable filler = tableRepository.findById(FILLER_TABLE_ID).orElseThrow(); + for (int i = 0; i < 4; i++) { + Reservation r = new Reservation(); + r.setUser(user); + r.setTable(filler); + r.setReservationDate(LocalDateTime.now().plusDays(10 + i)); + r.setNumberOfGuests(2); + r.setStatus(ReservationStatus.CONFIRMED); + reservationRepository.save(r); + } + } + } + + @Test + void createPersistsVipFlagOnTable() { + ReservationRequestDTO dto = new ReservationRequestDTO( + SEED_TABLE_ID, + LocalDateTime.now().plusDays(30).withHour(20).withMinute(0).withSecond(0).withNano(0), + 2, + true); + + reservationService.create(dto, "armymoves"); + + RestaurantTable reloaded = tableRepository.findById(SEED_TABLE_ID).orElseThrow(); + assertTrue(reloaded.getIsVip(), "is_vip debe persistirse en restaurant_tables"); + } + + @Test + @WithMockUser(username = "armymoves") + void cancelRollsBackPenaltyWhenLastWriteFails() { + doThrow(new DataIntegrityViolationException("fallo forzado")) + .when(reservationRepository).save(any(Reservation.class)); + + assertThrows(DataIntegrityViolationException.class, + () -> reservationService.cancelReservation(SEED_RESERVATION_ID)); + + AppUser after = appUserRepository.findById(SEED_USER_ID).orElseThrow(); + assertEquals(0, after.getPenalizationPoints(), + "los puntos deben revertirse si la cancelacion no se completa"); + } +} \ No newline at end of file From 200534247de9d507f5ca4c37a4f6b30dac6d39ed Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 5 Oct 2026 20:11:13 +0100 Subject: [PATCH 38/47] fix: make reservation mutations transactional --- .../com/pachedev/restoreserve/service/ReservationService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 6adf05b..dae1d63 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -24,6 +24,7 @@ import com.pachedev.restoreserve.repository.RestaurantTableRepository; import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor @@ -40,6 +41,7 @@ public class ReservationService { * - que una mesa previamente reservada estará ocupada por dos horas, por lo * que la hora de reserva solicitada no entrará en conflicto. */ + @Transactional public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUsername) { AppUser user = appUserRepository.findByUsername(currentUsername) @@ -192,6 +194,7 @@ public List findByStatus(ReservationStatus status) { * al cliente. * Cuando acumule más de 6 puntos, su estado pasará de ACTIVE a BANNED. */ + @Transactional public void cancelReservation(Long id) { Reservation reservation = getReservation(id); @@ -258,6 +261,7 @@ private Reservation getReservation(Long id) { * Modifica el estado de una reserva a COMPLETED si han pasado más de dos horas * de la hora de la reserva. */ + @Transactional public void completeReservation(Long id) { Reservation reservation = getReservation(id); From 5b62562f3ad8f26b67e4a2854acc29f7744601bd Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 5 Oct 2026 23:23:38 +0100 Subject: [PATCH 39/47] fix: lock table row when creating a reservation --- .../repository/RestaurantTableRepository.java | 20 +++++++++++++++---- .../service/ReservationService.java | 18 ++++++++--------- .../service/ReservationServiceTest.java | 2 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java index 028564c..40294f4 100644 --- a/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java +++ b/src/main/java/com/pachedev/restoreserve/repository/RestaurantTableRepository.java @@ -1,12 +1,16 @@ package com.pachedev.restoreserve.repository; -import java.util.List; - -import org.springframework.data.jpa.repository.JpaRepository; - import com.pachedev.restoreserve.model.entity.RestaurantTable; import com.pachedev.restoreserve.model.enums.TableLocation; import com.pachedev.restoreserve.model.enums.TableStatus; +import jakarta.persistence.LockModeType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Optional; public interface RestaurantTableRepository extends JpaRepository { @@ -19,4 +23,12 @@ public interface RestaurantTableRepository extends JpaRepository findByLocationAndStatus(TableLocation location, TableStatus status); List findByStatusAndMaxPaxGreaterThanEqual(TableStatus status, Integer maxPax); + + /** + * Bloquea la mesa mientras se crea la reserva para evitar que dos reservas + * simultáneas se realicen sobre la misma mesa. + */ + @Lock(LockModeType.PESSIMISTIC_WRITE) + @Query("select t from RestaurantTable t where t.id = :id") + Optional findByIdForUpdate(@Param("id") Long id); } diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index dae1d63..06609f1 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -1,13 +1,5 @@ package com.pachedev.restoreserve.service; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; - import com.pachedev.restoreserve.dto.ReservationRequestDTO; import com.pachedev.restoreserve.dto.ReservationResponseDTO; import com.pachedev.restoreserve.exception.BannedUserException; @@ -22,10 +14,16 @@ import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.repository.ReservationRepository; import com.pachedev.restoreserve.repository.RestaurantTableRepository; - import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + @Service @RequiredArgsConstructor public class ReservationService { @@ -56,7 +54,7 @@ public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUs throw new BusinessLogicException("La fecha y hora no pueden ser anteriores a la actual"); } - RestaurantTable table = tableRepository.findById(dto.tableId()) + RestaurantTable table = tableRepository.findByIdForUpdate(dto.tableId()) .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + dto.tableId() + " no encontrada")); if (dto.isVip() == true) { diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java index fef5f5e..7ae2231 100644 --- a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTest.java @@ -107,7 +107,7 @@ void createReservationExceedingPax() { table.setLocation(TableLocation.SALON); when(appUserRepository.findByUsername("tana")).thenReturn(Optional.of(user)); - when(tableRepository.findById(1L)).thenReturn(Optional.of(table)); + when(tableRepository.findByIdForUpdate(1L)).thenReturn(Optional.of(table)); assertThrows(BusinessLogicException.class, () -> reservationService.create(dto, "tana")); } From e6a6f0f13ae9e1af93ee10ac3800987870eb3ef7 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Mon, 5 Oct 2026 23:23:38 +0100 Subject: [PATCH 40/47] test: add regression test for duplicate booking --- .../service/ReservationServiceTxTest.java | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java index 225c5b6..e3b4518 100644 --- a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java @@ -1,22 +1,7 @@ package com.pachedev.restoreserve.service; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; - -import java.time.LocalDateTime; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.TestConstructor; -import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; - import com.pachedev.restoreserve.dto.ReservationRequestDTO; +import com.pachedev.restoreserve.exception.BusinessLogicException; import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.entity.Reservation; import com.pachedev.restoreserve.model.entity.RestaurantTable; @@ -25,6 +10,19 @@ import com.pachedev.restoreserve.repository.AppUserRepository; import com.pachedev.restoreserve.repository.ReservationRepository; import com.pachedev.restoreserve.repository.RestaurantTableRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.TestConstructor; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; @SpringBootTest(properties = "spring.datasource.url=jdbc:h2:mem:txtestdb") @TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL) @@ -106,4 +104,19 @@ void cancelRollsBackPenaltyWhenLastWriteFails() { assertEquals(0, after.getPenalizationPoints(), "los puntos deben revertirse si la cancelacion no se completa"); } -} \ No newline at end of file + + @Test + void createRejectsDoubleBookingOfSameSlot() { + ReservationRequestDTO slot = new ReservationRequestDTO( + SEED_TABLE_ID, + LocalDateTime.now().plusDays(45).withHour(21).withMinute(0).withSecond(0).withNano(0), + 2, + false); + + reservationService.create(slot, "armymoves"); + + assertThrows(BusinessLogicException.class, + () -> reservationService.create(slot, "armymoves"), + "una segunda reserva en el mismo hueco debe ser rechazada"); + } +} From f5089a1c954ede9b5e434ff4f59543171294b1d9 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 6 Oct 2026 10:05:00 +0100 Subject: [PATCH 41/47] test: assert vip table requires a habitual customer --- .../service/ReservationServiceTxTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java index e3b4518..71d4f2e 100644 --- a/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java +++ b/src/test/java/com/pachedev/restoreserve/service/ReservationServiceTxTest.java @@ -2,6 +2,7 @@ import com.pachedev.restoreserve.dto.ReservationRequestDTO; import com.pachedev.restoreserve.exception.BusinessLogicException; +import com.pachedev.restoreserve.exception.NotVipUserException; import com.pachedev.restoreserve.model.entity.AppUser; import com.pachedev.restoreserve.model.entity.Reservation; import com.pachedev.restoreserve.model.entity.RestaurantTable; @@ -119,4 +120,17 @@ void createRejectsDoubleBookingOfSameSlot() { () -> reservationService.create(slot, "armymoves"), "una segunda reserva en el mismo hueco debe ser rechazada"); } + + @Test + void createRejectsVipTableForNonHabitualUser() { + ReservationRequestDTO vip = new ReservationRequestDTO( + SEED_TABLE_ID, + LocalDateTime.now().plusDays(60).withHour(19).withMinute(0).withSecond(0).withNano(0), + 2, + true); + + assertThrows(NotVipUserException.class, + () -> reservationService.create(vip, "admin"), + "un usuario sin historial no debe poder reservar mesa VIP"); + } } From f360a3c78a51320812c7e215ab61c6ea8cdc5e47 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 6 Oct 2026 10:29:33 +0100 Subject: [PATCH 42/47] fix: scope the vip gate to the current user --- .../com/pachedev/restoreserve/service/ReservationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java index 06609f1..7dd3c3b 100644 --- a/src/main/java/com/pachedev/restoreserve/service/ReservationService.java +++ b/src/main/java/com/pachedev/restoreserve/service/ReservationService.java @@ -58,7 +58,7 @@ public ReservationResponseDTO create(ReservationRequestDTO dto, String currentUs .orElseThrow(() -> new ResourceNotFoundException("Mesa con id " + dto.tableId() + " no encontrada")); if (dto.isVip() == true) { - boolean isVipUser = reservationRepository.findByStatus(ReservationStatus.CONFIRMED).size() > 3; + boolean isVipUser = reservationRepository.findByUserIdAndStatus(user.getId(), ReservationStatus.CONFIRMED).size() > 3; if (!isVipUser) { throw new NotVipUserException("Sólo clientes habituales pueden reservar mesas VIP"); From 2591126c3f4cbebb263c0735b4ff21d6656c7277 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 6 Oct 2026 13:35:42 +0100 Subject: [PATCH 43/47] ci: run tests on every pull request --- .github/workflows/ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9ded242 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: CI + +on: + push: + branches: [develop, main] + pull_request: + branches: [develop, main] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + env: + JWT_SECRET: ci-only-secret-not-for-production-use-0123456789 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '17' + cache: maven + - name: Run tests + run: ./mvnw -B test \ No newline at end of file From ed6f5de9e4fa2efec79fa36788e4aa30eb820087 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 6 Oct 2026 16:28:48 +0100 Subject: [PATCH 44/47] docs: add ci status badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9b03df4..b916796 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # RestoReserve API +[![CI](https://github.com/cloudxdam/restoreserve-api/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/cloudxdam/restoreserve-api/actions/workflows/ci.yml) + RestoReserve API is a Spring Boot REST API for managing restaurant tables, reservations, and user access with JWT authentication. The project started as an academic project and is being refined with a focus on backend fundamentals: layered architecture, validation, security, persistence, testing, and business rules. From 1aa237ec8864e32128205138d4cf1322f922dd9d Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Tue, 6 Oct 2026 20:52:11 +0100 Subject: [PATCH 45/47] refactor: seed demo data through a conditional runner --- .../restoreserve/config/DataSeeder.java | 81 +++++++++++++++++++ src/main/resources/application.properties | 1 - src/main/resources/data.sql | 25 ------ 3 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/pachedev/restoreserve/config/DataSeeder.java delete mode 100644 src/main/resources/data.sql diff --git a/src/main/java/com/pachedev/restoreserve/config/DataSeeder.java b/src/main/java/com/pachedev/restoreserve/config/DataSeeder.java new file mode 100644 index 0000000..09f7294 --- /dev/null +++ b/src/main/java/com/pachedev/restoreserve/config/DataSeeder.java @@ -0,0 +1,81 @@ +package com.pachedev.restoreserve.config; + +import com.pachedev.restoreserve.model.entity.AppUser; +import com.pachedev.restoreserve.model.entity.Reservation; +import com.pachedev.restoreserve.model.entity.RestaurantTable; +import com.pachedev.restoreserve.model.enums.*; +import com.pachedev.restoreserve.repository.AppUserRepository; +import com.pachedev.restoreserve.repository.ReservationRepository; +import com.pachedev.restoreserve.repository.RestaurantTableRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Component +@RequiredArgsConstructor +public class DataSeeder implements CommandLineRunner { + + private final AppUserRepository userRepository; + private final RestaurantTableRepository restaurantTableRepository; + private final ReservationRepository reservationRepository; + + @Override + @Transactional + public void run(String... args) throws Exception { + if (userRepository.count() > 0) { + return; + } + + RestaurantTable mesa1 = createTable("Mesa 1", 4, TableStatus.AVAILABLE, TableLocation.SALON, false); + RestaurantTable mesa2 = createTable("Mesa 2", 2, TableStatus.RESERVED, TableLocation.TERRACE, false); + RestaurantTable mesa3 = createTable("Mesa 3", 6, TableStatus.AVAILABLE, TableLocation.SALON, false); + RestaurantTable mesa4 = createTable("Mesa 4", 8, TableStatus.RESERVED, TableLocation.TERRACE, false); + + AppUser armymoves = createUser("Armiche Santana", "6123456789", "armiche@gmail.com", "armymoves", + "$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi", UserRole.ROLE_USER); + AppUser admin = createUser("Acaymo Bello", "600000000", "acaymo@gmail.com", "admin", + "$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..", UserRole.ROLE_ADMIN); + + createReservation(armymoves, mesa1, LocalDateTime.of(2026, 5, 16, 20, 0), 2); + createReservation(admin, mesa2, LocalDateTime.of(2026, 5, 16, 15, 0), 2); + } + + private RestaurantTable createTable(String name, Integer maxPax, TableStatus status, + TableLocation location, Boolean isVip) { + RestaurantTable table = new RestaurantTable(); + table.setName(name); + table.setMaxPax(maxPax); + table.setStatus(status); + table.setLocation(location); + table.setIsVip(isVip); + return restaurantTableRepository.save(table); + } + + private AppUser createUser(String name, String telephone, String email, String username, + String password, UserRole role) { + AppUser user = new AppUser(); + user.setName(name); + user.setTelephone(telephone); + user.setEmail(email); + user.setUsername(username); + user.setPassword(password); + user.setRole(role); + user.setPenalizationPoints(0); + user.setStatus(UserStatus.ACTIVE); + return userRepository.save(user); + } + + private void createReservation(AppUser user, RestaurantTable table, + LocalDateTime reservationDate, Integer numberOfGuests) { + Reservation reservation = new Reservation(); + reservation.setUser(user); + reservation.setTable(table); + reservation.setReservationDate(reservationDate); + reservation.setNumberOfGuests(numberOfGuests); + reservation.setStatus(ReservationStatus.CONFIRMED); + reservationRepository.save(reservation); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 98d8cab..08609f1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,7 +9,6 @@ spring.datasource.password= spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true -spring.jpa.defer-datasource-initialization=true jwt.secret-key=${JWT_SECRET} jwt.expiration=86400000 diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql deleted file mode 100644 index 1ae8510..0000000 --- a/src/main/resources/data.sql +++ /dev/null @@ -1,25 +0,0 @@ -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 1', 4, 'AVAILABLE', 'SALON', false); - -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 2', 2, 'RESERVED', 'TERRACE', false); - -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 3', 6, 'AVAILABLE', 'SALON', false); - -INSERT INTO restaurant_tables (name, max_pax, status, location, is_vip) -VALUES ('Mesa 4', 8, 'RESERVED', 'TERRACE', false); - - -- test credentials-> username: armymoves - password: 1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) -VALUES ('Armiche Santana','6123456789','armiche@gmail.com','armymoves', '$2a$12$UfijVlkvrmwpTGERWsfsCe.R5ZIPaJPCP7TmjsnRMYtE8USwgsixi','ROLE_USER',true, 0, 'ACTIVE'); - --- test credentials-> username: admin - password: Admin1234 -INSERT INTO users (name, telephone, email, user_name, password, role, active, penalization_points, user_status) -VALUES ('Acaymo Bello','600000000','acaymo@gmail.com','admin', '$2a$12$lAUz1WTEed/7SOsy.ew.OehjCeESngVWkgkc0fqRgZqeo/W4AwM..','ROLE_ADMIN',true, 0, 'ACTIVE'); - -INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (1, 1,'2026-05-16T20:00:00', CURRENT_TIMESTAMP, 2,'CONFIRMED'); - -INSERT INTO reservations (user_id, table_id, reservation_date, created_at, number_of_guests, status) -VALUES (2, 2, '2026-05-16T15:00:00', CURRENT_TIMESTAMP, 2, 'CONFIRMED'); \ No newline at end of file From 1bc09b59f66f53679e8e1dd2b9e6d29809fca337 Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Wed, 7 Oct 2026 09:54:19 +0100 Subject: [PATCH 46/47] feat: add postgres profile with docker compose --- .env.example | 5 +++++ docker-compose.yml | 21 +++++++++++++++++++ pom.xml | 5 +++++ .../resources/application-postgres.properties | 3 +++ 4 files changed, 34 insertions(+) create mode 100644 docker-compose.yml create mode 100644 src/main/resources/application-postgres.properties diff --git a/.env.example b/.env.example index 9c3b364..55b3f79 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,8 @@ # Required environment variables. # See "Configure the JWT secret" in README.md to generate .env with a real value. JWT_SECRET= + +# PostgreSQL (only needed for the `postgres` profile). +POSTGRES_DB=restoreserve +POSTGRES_USER=restoreserve +POSTGRES_PASSWORD= \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..09f6774 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +services: + db: + image: postgres:17-alpine + container_name: restoreserve-db + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:?POSTGRES_DB is not set in .env} + POSTGRES_USER: ${POSTGRES_USER:?POSTGRES_USER is not set in .env} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is not set in .env} + ports: + - "5432:5432" + volumes: + - restoreserve-pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 10 + +volumes: + restoreserve-pgdata: \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1d1dcdb..be1bb0e 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,11 @@ h2 runtime + + org.postgresql + postgresql + runtime + org.projectlombok lombok diff --git a/src/main/resources/application-postgres.properties b/src/main/resources/application-postgres.properties new file mode 100644 index 0000000..2b7de84 --- /dev/null +++ b/src/main/resources/application-postgres.properties @@ -0,0 +1,3 @@ +spring.datasource.url=jdbc:postgresql://localhost:5432/${POSTGRES_DB:restoreserve} +spring.datasource.username=${POSTGRES_USER:restoreserve} +spring.datasource.password=${POSTGRES_PASSWORD} \ No newline at end of file From 6d8c78c4e192dd9c847c273c3f0c83fed19cb18f Mon Sep 17 00:00:00 2001 From: cloudxdam Date: Wed, 7 Oct 2026 10:22:10 +0100 Subject: [PATCH 47/47] docs: document the postgres profile --- README.md | 60 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index b916796..062a0ce 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ RestoReserve API is a Spring Boot REST API for managing restaurant tables, reservations, and user access with JWT authentication. -The project started as an academic project and is being refined with a focus on backend fundamentals: layered architecture, validation, security, persistence, testing, and business rules. +The project started as an academic project and has since been refined with a focus on backend fundamentals: layered architecture, validation, security, persistence, testing, and business rules. ## Tech Stack @@ -13,14 +13,16 @@ The project started as an academic project and is being refined with a focus on - Spring Web - Spring Security - Spring Data JPA -- H2 Database +- H2 Database (default profile) +- PostgreSQL (`postgres` profile, optional) - JJWT - Spring Validation -- Springdoc OpenAPIz +- Springdoc OpenAPI - Lombok - JUnit 5 + Mockito - Spring Security Test - Maven +- Docker Compose ## Main Features @@ -123,22 +125,25 @@ Custom exceptions include: - Java 17 - Maven Wrapper included in the project +- Docker (only for the optional PostgreSQL profile) -### Configure the JWT secret +### Configure the environment The application signs its tokens with a key read from the `JWT_SECRET` environment -variable. The key is not stored in the repository, so the application will not start -until you provide one. +variable. Neither the key nor the database credentials are stored in the repository, +so the application will not start until you provide them. -Create a local `.env` file (already ignored by git) with a freshly generated key: +Create a local `.env` file (already ignored by git) with freshly generated values: ```bash -printf 'JWT_SECRET=%s\n' "$(openssl rand -base64 64)" > .env +printf 'JWT_SECRET=%s\nPOSTGRES_DB=restoreserve\nPOSTGRES_USER=restoreserve\nPOSTGRES_PASSWORD=%s\n' \ + "$(openssl rand -base64 64)" "$(openssl rand -hex 16)" > .env ``` -The required variables are documented in `.env.example`. Never commit `.env`. +The command overwrites any existing `.env`. The required variables are documented in +`.env.example`. Never commit `.env`. -### Start the application +### Start the application (H2, default) Linux / macOS: @@ -152,12 +157,30 @@ Windows: mvnw.cmd spring-boot:run ``` -The application uses an in-memory H2 database with seed data loaded from: +### Start the application with PostgreSQL (optional) -```text -src/main/resources/data.sql +Start the database and wait until it reports `healthy`: + +```bash +docker compose up -d +docker compose ps +``` + +Then start the application: + +```bash +./mvnw spring-boot:run -Dspring-boot.run.profiles=postgres +``` + +Stop the database when you are done: + +```bash +docker compose down ``` +Data lives in a named volume and survives container restarts. Use `docker compose down -v` +only if you want to delete it. + ## Swagger UI Once the application is running, open: @@ -234,17 +257,16 @@ Run the test suite with: ## Notes About the Current Setup -- H2 is used as a lightweight local development database. -- Demo data is loaded from `data.sql`. +- The default profile runs on an in-memory H2 database; the optional `postgres` profile runs on PostgreSQL through Docker Compose. +- Demo data is seeded on startup by `config/DataSeeder` and is skipped when users already exist, so restarting the application does not duplicate it. - JWT authentication is implemented with JJWT and Spring Security. - The project is currently focused on local execution and portfolio presentation. ## Future Improvements - Add more controller, security, and integration tests. -- Improve transaction boundaries and testability in the reservation service. -- Add database profiles for H2 and PostgreSQL. -- Improve repository and development workflow. +- Containerize the API with a Dockerfile and an `app` service in compose. +- Replace `ddl-auto=update` with Flyway migrations. - Add production-oriented configuration and observability. ## Why This Project Matters @@ -271,4 +293,4 @@ It provides a foundation for demonstrating junior backend development skills wit **Daniel Pacheco** -GitHub: `cloudxdam` +GitHub: [cloudxdam](https://github.com/cloudxdam)