11package dev .matheuscruz .presentation ;
22
3- import dev .matheuscruz .domain .* ;
3+ import dev .matheuscruz .domain .AmountAndTypeOnly ;
44import dev .matheuscruz .domain .Record ;
5+ import dev .matheuscruz .domain .RecordRepository ;
6+ import dev .matheuscruz .domain .Transactions ;
7+ import dev .matheuscruz .domain .User ;
8+ import dev .matheuscruz .domain .UserRepository ;
9+ import dev .matheuscruz .presentation .data .CreateRecordRequest ;
10+ import dev .matheuscruz .presentation .data .PageRecord ;
11+ import dev .matheuscruz .presentation .data .RecordItemResponse ;
512import io .quarkus .narayana .jta .QuarkusTransaction ;
613import io .quarkus .panache .common .Page ;
714import io .quarkus .panache .common .Parameters ;
8- import jakarta .transaction .Transactional ;
15+ import io .quarkus .security .Authenticated ;
16+ import jakarta .enterprise .context .RequestScoped ;
917import jakarta .validation .Valid ;
10- import jakarta .validation .constraints .NotBlank ;
11- import jakarta .validation .constraints .NotNull ;
12- import jakarta .validation .constraints .PositiveOrZero ;
1318import jakarta .ws .rs .DELETE ;
1419import jakarta .ws .rs .ForbiddenException ;
1520import jakarta .ws .rs .GET ;
1924import jakarta .ws .rs .core .Response ;
2025import java .math .BigDecimal ;
2126import java .net .URI ;
22- import java .time .Instant ;
23- import java .time .LocalDateTime ;
2427import java .time .ZoneId ;
25- import java .time .ZoneOffset ;
2628import java .time .format .DateTimeFormatter ;
2729import java .util .List ;
2830import java .util .Optional ;
31+ import org .eclipse .microprofile .jwt .Claim ;
32+ import org .eclipse .microprofile .jwt .Claims ;
2933import org .jboss .resteasy .reactive .RestQuery ;
3034
35+ @ RequestScoped
3136@ Path ("/api/records" )
37+ @ Authenticated
3238public class RecordResource {
3339
40+ static DateTimeFormatter formatter = DateTimeFormatter .ofPattern ("dd/MM/yyyy" );
41+
42+ @ Claim (standard = Claims .upn )
43+ String upn ;
44+
3445 RecordRepository recordRepository ;
3546 UserRepository userRepository ;
36- static DateTimeFormatter formatter = DateTimeFormatter .ofPattern ("dd/MM/yyyy" );
37- static Instant INSTANT_2025 = LocalDateTime .of (2025 , 1 , 1 , 0 , 0 , 0 ).toInstant (ZoneOffset .UTC );
3847
3948 public RecordResource (RecordRepository recordRepository , UserRepository userRepository ) {
4049 this .recordRepository = recordRepository ;
@@ -43,9 +52,9 @@ public RecordResource(RecordRepository recordRepository, UserRepository userRepo
4352
4453 @ DELETE
4554 @ Path ("/{id}" )
46- @ Transactional
4755 public Response delete (@ PathParam ("id" ) Long id ) {
48- recordRepository .deleteById (id );
56+ QuarkusTransaction .requiringNew ().run (() -> recordRepository .delete ("id = :id AND userId = :userId" ,
57+ Parameters .with ("id" , id ).and ("userId" , upn )));
4958 return Response .status (Response .Status .NO_CONTENT ).build ();
5059 }
5160
@@ -56,8 +65,11 @@ public Response createRecord(@Valid CreateRecordRequest req) {
5665 Record record = new Record .Builder ().userId (user .getId ()).amount (req .amount ()).description (req .description ())
5766 .transaction (req .transaction ()).category (req .category ()).build ();
5867
59- QuarkusTransaction .requiringNew ().run (() -> this .recordRepository .persist (record ));
68+ if (!user .getId ().equals (upn )) {
69+ return Response .status (Response .Status .FORBIDDEN ).build ();
70+ }
6071
72+ QuarkusTransaction .requiringNew ().run (() -> this .recordRepository .persist (record ));
6173 return Response .created (URI .create ("/api/records/" + record .getId ())).build ();
6274 }
6375
@@ -67,40 +79,29 @@ public Response getRecords(@RestQuery("page") String p, @RestQuery("limit") Stri
6779 int page = Integer .parseInt (Optional .of (p ).orElse ("0" ));
6880 int limit = Integer .parseInt (Optional .of (l ).orElse ("10" ));
6981
70- long totalRecords = recordRepository .count ();
82+ // TODO: https://github.com/mcruzdev/timeless/issues/125
83+ long totalRecords = recordRepository .count ("userId = :userId" , Parameters .with ("userId" , upn ));
7184
72- List <RecordItemResponse > output = recordRepository .findAll ().page (Page .of (page , limit )).list ().stream ()
73- .map (record -> {
85+ // pagination
86+ List <RecordItemResponse > output = recordRepository .find ("userId = :userId" , Parameters .with ("userId" , upn ))
87+ .page (Page .of (page , limit )).list ().stream ().map (record -> {
7488 String format = record .getCreatedAt ().atZone (ZoneId .of ("America/Sao_Paulo" )).toLocalDate ()
7589 .format (formatter );
7690 return new RecordItemResponse (record .getId (), record .getAmount (), record .getDescription (),
7791 record .getTransaction ().name (), format , record .getCategory ().name ());
7892 }).toList ();
7993
80- List <Record > list = recordRepository .find ("createdAt >= :instant AND createdAt <= :now" ,
81- Parameters .with ("instant" , INSTANT_2025 ).and ("now" , Instant .now ())).list ();
82-
83- Optional <BigDecimal > totalExpenses = list .stream ()
84- .filter (item -> item .getTransaction ().equals (Transactions .OUT )).map (Record ::getAmount )
94+ // calculate total expenses and total in
95+ List <AmountAndTypeOnly > amountAndType = recordRepository .getRecordsWithAmountAndTypeOnlyByUser (upn );
96+ Optional <BigDecimal > totalExpenses = amountAndType .stream ()
97+ .filter (item -> item .getTransaction ().equals (Transactions .OUT )).map (AmountAndTypeOnly ::getAmount )
8598 .reduce (BigDecimal ::add );
8699
87- Optional <BigDecimal > totalIn = list .stream ().filter (item -> item .getTransaction ().equals (Transactions .IN ))
88- .map (Record ::getAmount ).reduce (BigDecimal ::add );
100+ Optional <BigDecimal > totalIn = amountAndType .stream ()
101+ .filter (item -> item .getTransaction ().equals (Transactions .IN )).map (AmountAndTypeOnly ::getAmount )
102+ .reduce (BigDecimal ::add );
89103
90- return Response .ok (new PagedRecord (output , totalRecords , totalExpenses .orElse (BigDecimal .ZERO ),
104+ return Response .ok (new PageRecord (output , totalRecords , totalExpenses .orElse (BigDecimal .ZERO ),
91105 totalIn .orElse (BigDecimal .ZERO ))).build ();
92106 }
93-
94- public record PagedRecord (List <RecordItemResponse > items , Long totalRecords , BigDecimal totalExpenses ,
95- BigDecimal totalIn ) {
96- }
97-
98- public record RecordItemResponse (Long id , BigDecimal amount , String description , String transaction ,
99- String createdAt , String category ) {
100- }
101-
102- public record CreateRecordRequest (@ PositiveOrZero BigDecimal amount , @ NotBlank String description ,
103- @ NotNull Transactions transaction , @ NotBlank String from , @ NotNull Categories category ) {
104- }
105-
106107}
0 commit comments