diff --git a/http-requests/cart.http b/http-requests/cart.http
new file mode 100644
index 0000000..1556750
--- /dev/null
+++ b/http-requests/cart.http
@@ -0,0 +1,21 @@
+### add
+POST {{url}}/cart/add
+Content-Type: application/json
+
+{
+ "id": 9999999,
+ "cartEntries": [
+ {
+ "id": 2,
+ "amount": 20
+ }
+ ]
+}
+
+### list
+GET {{url}}/user/signin
+
+
+### checkout
+GET {{url}}/user/update
+Authorization: Bearer {{token}}
\ No newline at end of file
diff --git a/http-requests/category.http b/http-requests/category.http
new file mode 100644
index 0000000..0df78f1
--- /dev/null
+++ b/http-requests/category.http
@@ -0,0 +1,34 @@
+### create
+POST {{url}}/category/create
+Authorization: Bearer {{token}}
+Content-Type: application/json
+
+{
+ "name": "Erwachsene",
+ "price": 25.5,
+ "stock": 100
+}
+
+### update
+PUT {{url}}/category/update
+Authorization: Bearer {{token}}
+Content-Type: application/json
+
+{
+ "id": 1,
+ "name": "Erwachsene",
+ "price": 25.5,
+ "stock": 100
+}
+
+### delete
+DELETE {{url}}/category/delete/1
+Authorization: Bearer {{token}}
+
+### list
+GET {{url}}/category/list
+Authorization: Bearer {{token}}
+
+### load
+GET {{url}}/category/2
+Authorization: Bearer {{token}}
diff --git a/http-requests/event.http b/http-requests/event.http
new file mode 100644
index 0000000..f83572a
--- /dev/null
+++ b/http-requests/event.http
@@ -0,0 +1,53 @@
+### create
+POST {{url}}/event/create
+# Authorization: Bearer {{token}}
+Content-Type: application/json
+
+{
+ "name": "Maturaball HTL Steyr 2024",
+ "from": "2023-03-02T20:00:00.000Z",
+ "to": "2023-03-03T05:00:00.000Z",
+ "description": "Maturaball der Abteilungen EL, IT, ME, Y",
+ "ticketCategories": [
+ {
+ "name": "Normal",
+ "price": 25,
+ "stock": 500
+ },
+ {
+ "name": "Maturanten",
+ "price": 0,
+ "stock": 500
+ },
+ {
+ "name": "Prechtl",
+ "price": 50,
+ "stock": 1
+ }
+ ]
+}
+
+### update
+PUT {{url}}/event/update
+Authorization: Bearer {{token}}
+Content-Type: application/json
+
+{
+ "id": 2,
+ "name": "Maturaball HTL Steyr 2024",
+ "from": "2024-03-02T20:00:00.000Z",
+ "to": "2024-03-03T05:00:00.000Z",
+ "description": "Fluch der HTL"
+}
+
+### delete
+DELETE {{url}}/event/delete/3
+Authorization: Bearer {{token}}
+
+### list future events
+GET {{url}}/event/list
+Authorization: Bearer {{token}}
+
+### load
+GET {{url}}/event/2
+Authorization: Bearer {{token}}
diff --git a/http-requests/http-client.env.json b/http-requests/http-client.env.json
index 9dd49de..1d5dd67 100644
--- a/http-requests/http-client.env.json
+++ b/http-requests/http-client.env.json
@@ -1,5 +1,6 @@
{
"dev": {
- "url": "http://localhost:8080/api/v1"
+ "url": "http://localhost:8080/api/v1",
+ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZXRpY2tldCB1c2VyIHRva2VuIiwiZXhwIjoxNzAzMTAwNzQyLCJ1c2VySWQiOjIsImlhdCI6MTcwMzAxNDM0Mn0.D9HBxWy1vIP82XOh_ocjLO9HB0lK_rQGjgD3a7KQrOE"
}
}
\ No newline at end of file
diff --git a/http-requests/user.http b/http-requests/user.http
index bed80a3..ec6afb4 100644
--- a/http-requests/user.http
+++ b/http-requests/user.http
@@ -10,22 +10,34 @@ Content-Type: application/json
}
### signin
-GET {{url}}/user/signin
+POST {{url}}/user/signin
+Content-Type: application/json
+
+{
+ "email": "admin@email.com",
+ "password": "a"
+}
+
### update
PUT {{url}}/user/update
+Authorization: Bearer {{token}}
+Content-Type: application/json
{
-
+ "email": "ratp@htl-steyr.ac.at",
+ "firstname": "Peter Chef",
+ "lastname": "Rathgeb"
}
### delete
-DELETE {{url}}/user/delete/99
+DELETE {{url}}/user/delete/1
+Authorization: Bearer {{token}}
### list
GET {{url}}/user/list
+Authorization: Bearer {{token}}
### load
-GET {{url}}/user/load/1
-
-# eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZXRpY2tldCB1c2VyIHRva2VuIiwiZXhwIjoxNzAzMDU4MjczLCJ1c2VySWQiOjEsImlhdCI6MTcwMjk3MTg3M30.hC1N7hKYSIT-fqmaZ9bv3-YXOxQWdp-Sb5rZi4rARc0
+GET {{url}}/user/load/2
+Authorization: Bearer {{token}}
diff --git a/pom.xml b/pom.xml
index 3ac91fc..47d3379 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
com.auth0
java-jwt
diff --git a/src/main/java/me/jweissen/aeticket/aspect/AdminOnly.java b/src/main/java/me/jweissen/aeticket/aspect/AdminOnly.java
new file mode 100644
index 0000000..6c9aea9
--- /dev/null
+++ b/src/main/java/me/jweissen/aeticket/aspect/AdminOnly.java
@@ -0,0 +1,11 @@
+package me.jweissen.aeticket.aspect;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AdminOnly {
+}
diff --git a/src/main/java/me/jweissen/aeticket/aspect/AuthAspect.java b/src/main/java/me/jweissen/aeticket/aspect/AuthAspect.java
new file mode 100644
index 0000000..5c26608
--- /dev/null
+++ b/src/main/java/me/jweissen/aeticket/aspect/AuthAspect.java
@@ -0,0 +1,63 @@
+package me.jweissen.aeticket.aspect;
+
+import jakarta.servlet.http.HttpServletRequest;
+import me.jweissen.aeticket.model.Role;
+import me.jweissen.aeticket.model.User;
+import me.jweissen.aeticket.service.AuthService;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Aspect
+@Component
+public class AuthAspect {
+ private final HttpServletRequest request;
+ private final AuthService authService;
+
+ public AuthAspect(HttpServletRequest request, AuthService authService) {
+ this.request = request;
+ this.authService = authService;
+ }
+
+ @Pointcut("@annotation(UserOnly)")
+ public void userOnly() { }
+
+ @Pointcut("@annotation(AdminOnly)")
+ public void adminOnly() { }
+
+ public Optional authenticate() {
+ String tokenPrefix = "Bearer ";
+ String authHeader = request.getHeader("Authorization");
+ if (authHeader == null || !authHeader.startsWith(tokenPrefix)) {
+ return Optional.empty();
+ }
+ String token = authHeader.substring(tokenPrefix.length());
+ return authService.authenticate(token);
+ }
+
+ @Around("userOnly()")
+ public Object checkUser(ProceedingJoinPoint pjp) throws Throwable {
+ Optional user = authenticate();
+ if (user.isEmpty()) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
+ }
+ return pjp.proceed();
+ }
+
+ @Around("adminOnly()")
+ public Object checkAdmin(ProceedingJoinPoint pjp) throws Throwable {
+ Optional user = authenticate();
+ if (user.isEmpty()) {
+ return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
+ } else if (user.get().getRole() != Role.ADMIN) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+ return pjp.proceed();
+ }
+}
diff --git a/src/main/java/me/jweissen/aeticket/aspect/UserOnly.java b/src/main/java/me/jweissen/aeticket/aspect/UserOnly.java
new file mode 100644
index 0000000..043470c
--- /dev/null
+++ b/src/main/java/me/jweissen/aeticket/aspect/UserOnly.java
@@ -0,0 +1,11 @@
+package me.jweissen.aeticket.aspect;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UserOnly {
+}
diff --git a/src/main/java/me/jweissen/aeticket/controller/CartController.java b/src/main/java/me/jweissen/aeticket/controller/CartController.java
index c8bcc52..104669c 100644
--- a/src/main/java/me/jweissen/aeticket/controller/CartController.java
+++ b/src/main/java/me/jweissen/aeticket/controller/CartController.java
@@ -1,15 +1,13 @@
package me.jweissen.aeticket.controller;
import me.jweissen.aeticket.dto.request.CartAddRequestDto;
-import me.jweissen.aeticket.dto.response.CartEntryResponseDto;
+import me.jweissen.aeticket.dto.response.CartEventResponseDto;
import me.jweissen.aeticket.dto.response.CheckoutResponseDto;
+import me.jweissen.aeticket.service.AuthService;
import me.jweissen.aeticket.service.CartService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -17,26 +15,29 @@ import java.util.List;
@RequestMapping("/cart")
public class CartController {
private final CartService cartService;
+ private final AuthService authService;
- public CartController(CartService cartService) {
+ public CartController(CartService cartService, AuthService authService) {
this.cartService = cartService;
+ this.authService = authService;
}
@PostMapping("/add")
- public ResponseEntity addEntry(CartAddRequestDto dto) {
- // TODO
- return ResponseEntity.noContent().build();
+ public ResponseEntity addEntry(@RequestBody CartAddRequestDto dto) {
+ if (!cartService.add(dto, authService.getCurrentUser().getCurrentCart())) {
+ // user gave invalid category id(s)
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
+ }
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@GetMapping("/list")
- public ResponseEntity> getCartEntries() {
- // TODO
- return new ResponseEntity<>(null, HttpStatus.OK);
+ public ResponseEntity> getCartEntries() {
+ return new ResponseEntity<>(cartService.toDto(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK);
}
@GetMapping("/checkout")
public ResponseEntity checkout() {
- // TODO
- return ResponseEntity.badRequest().build();
+ return new ResponseEntity<>(cartService.checkout(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK);
}
}
diff --git a/src/main/java/me/jweissen/aeticket/controller/CategoryController.java b/src/main/java/me/jweissen/aeticket/controller/CategoryController.java
index 969e6bb..aabfc5f 100644
--- a/src/main/java/me/jweissen/aeticket/controller/CategoryController.java
+++ b/src/main/java/me/jweissen/aeticket/controller/CategoryController.java
@@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
-@RequestMapping("/eventcategory")
+@RequestMapping("/category")
public class CategoryController {
private final CategoryService categoryService;
@@ -26,35 +26,35 @@ public class CategoryController {
public ResponseEntity create(@RequestBody CategoryRequestDto dto) {
// TODO admin only
categoryService.create(dto);
- return new ResponseEntity<>(HttpStatus.CREATED);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
}
@PutMapping("/update")
public ResponseEntity update(@RequestBody CategoryUpdateRequestDto dto) {
// TODO admin only
+ System.out.println(dto);
if (!categoryService.update(dto)) {
- return ResponseEntity.notFound().build();
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
- return new ResponseEntity<>(HttpStatus.CREATED);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
}
@DeleteMapping("/delete/{id}")
public ResponseEntity delete(@PathVariable Long id) {
// TODO admin only
categoryService.delete(id);
- return ResponseEntity.noContent().build();
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@GetMapping("/{id}")
public ResponseEntity getById(@PathVariable Long id) {
return categoryService.getById(id)
.map(categoryResponseDto -> new ResponseEntity<>(categoryResponseDto, HttpStatus.OK))
- .orElseGet(() -> ResponseEntity.notFound().build());
+ .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
}
@GetMapping("/list")
public ResponseEntity> getAll() {
return new ResponseEntity<>(categoryService.getAll(), HttpStatus.OK);
}
-
}
diff --git a/src/main/java/me/jweissen/aeticket/controller/EventController.java b/src/main/java/me/jweissen/aeticket/controller/EventController.java
index 752c1c8..623a911 100644
--- a/src/main/java/me/jweissen/aeticket/controller/EventController.java
+++ b/src/main/java/me/jweissen/aeticket/controller/EventController.java
@@ -23,28 +23,28 @@ public class EventController {
public ResponseEntity create(@RequestBody EventRequestDto event) {
// TODO admin only
eventService.create(event);
- return new ResponseEntity<>(HttpStatus.CREATED);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
}
@PutMapping("/update")
public ResponseEntity update(@RequestBody EventUpdateRequestDto event) {
// TODO admin only
eventService.update(event);
- return new ResponseEntity<>(HttpStatus.CREATED);
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@DeleteMapping("/delete/{id}")
public ResponseEntity delete(@PathVariable Long id) {
// TODO admin only
eventService.delete(id);
- return ResponseEntity.noContent().build();
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@GetMapping("/{id}")
public ResponseEntity getById(@PathVariable Long id) {
return eventService.getById(id)
.map(eventResponseDto -> new ResponseEntity<>(eventResponseDto, HttpStatus.OK))
- .orElseGet(() -> ResponseEntity.notFound().build());
+ .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
}
@GetMapping("/list")
diff --git a/src/main/java/me/jweissen/aeticket/controller/UserController.java b/src/main/java/me/jweissen/aeticket/controller/UserController.java
index 577eee6..e1d1d32 100644
--- a/src/main/java/me/jweissen/aeticket/controller/UserController.java
+++ b/src/main/java/me/jweissen/aeticket/controller/UserController.java
@@ -1,6 +1,5 @@
package me.jweissen.aeticket.controller;
-import me.jweissen.aeticket.aspect.Permissions;
import me.jweissen.aeticket.dto.request.LoginRequestDto;
import me.jweissen.aeticket.dto.request.SignupRequestDto;
import me.jweissen.aeticket.dto.request.UserUpdateRequestDto;
@@ -39,30 +38,29 @@ public class UserController {
public ResponseEntity update(@RequestBody UserUpdateRequestDto user) {
// TODO admin only
if (!userService.update(user)) {
- return ResponseEntity.notFound().build();
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
- return ResponseEntity.noContent().build();
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@DeleteMapping("/delete/{id}")
- public ResponseEntity delete(@PathVariable Integer id) {
+ public ResponseEntity delete(@PathVariable Long id) {
// TODO admin only
userService.delete(id);
- return ResponseEntity.noContent().build();
+ return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
@GetMapping("/list")
- @Permissions(user = false)
public ResponseEntity> getAll() {
// TODO admin only
return new ResponseEntity<>(userService.getAll(), HttpStatus.OK);
}
@GetMapping("/load/{id}")
- public ResponseEntity getById(@PathVariable Integer id) {
+ public ResponseEntity getById(@PathVariable Long id) {
// TODO admin only
return userService.getById(id)
.map(userResponseDto -> new ResponseEntity<>(userResponseDto, HttpStatus.OK))
- .orElseGet(() -> new ResponseEntity<>(null, HttpStatus.NOT_FOUND));
+ .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build());
}
}
diff --git a/src/main/java/me/jweissen/aeticket/dto/request/EventUpdateRequestDto.java b/src/main/java/me/jweissen/aeticket/dto/request/EventUpdateRequestDto.java
index a64cc32..c12d7c3 100644
--- a/src/main/java/me/jweissen/aeticket/dto/request/EventUpdateRequestDto.java
+++ b/src/main/java/me/jweissen/aeticket/dto/request/EventUpdateRequestDto.java
@@ -1,4 +1,7 @@
package me.jweissen.aeticket.dto.request;
-public record EventUpdateRequestDto(Long id) {
+import java.util.Date;
+
+// no ticketCategories to prevent undefined behaviour
+public record EventUpdateRequestDto(Long id, String name, Date from, Date to, String description) {
}
diff --git a/src/main/java/me/jweissen/aeticket/dto/request/UserUpdateRequestDto.java b/src/main/java/me/jweissen/aeticket/dto/request/UserUpdateRequestDto.java
index 70c7265..157bee9 100644
--- a/src/main/java/me/jweissen/aeticket/dto/request/UserUpdateRequestDto.java
+++ b/src/main/java/me/jweissen/aeticket/dto/request/UserUpdateRequestDto.java
@@ -1,4 +1,4 @@
package me.jweissen.aeticket.dto.request;
-public record UserUpdateRequestDto(Integer id, String firstname, String lastname, String email) {
+public record UserUpdateRequestDto(Long id, String firstname, String lastname, String email) {
}
diff --git a/src/main/java/me/jweissen/aeticket/model/Cart.java b/src/main/java/me/jweissen/aeticket/model/Cart.java
index 50c97d3..06c1459 100644
--- a/src/main/java/me/jweissen/aeticket/model/Cart.java
+++ b/src/main/java/me/jweissen/aeticket/model/Cart.java
@@ -15,6 +15,9 @@ public class Cart {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+ @Column(nullable = false)
+ private Boolean checkedOut = false;
+
@OneToMany(mappedBy = "cart")
@Column(nullable = false)
private List cartEntries;
diff --git a/src/main/java/me/jweissen/aeticket/model/CartEntry.java b/src/main/java/me/jweissen/aeticket/model/CartEntry.java
index d334034..1f9740c 100644
--- a/src/main/java/me/jweissen/aeticket/model/CartEntry.java
+++ b/src/main/java/me/jweissen/aeticket/model/CartEntry.java
@@ -1,26 +1,30 @@
package me.jweissen.aeticket.model;
import jakarta.persistence.*;
-import lombok.Getter;
-import lombok.Setter;
+import lombok.*;
@Entity
@Table
@Getter
@Setter
+@NoArgsConstructor
+@RequiredArgsConstructor
public class CartEntry {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@ManyToOne
+ @NonNull
@JoinColumn(nullable = false)
private Cart cart;
@ManyToOne
+ @NonNull
@JoinColumn(nullable = false)
private Category category;
@Column(nullable = false)
- private int amount;
+ @NonNull
+ private Integer amount;
}
diff --git a/src/main/java/me/jweissen/aeticket/model/Event.java b/src/main/java/me/jweissen/aeticket/model/Event.java
index b597b3c..5acabbb 100644
--- a/src/main/java/me/jweissen/aeticket/model/Event.java
+++ b/src/main/java/me/jweissen/aeticket/model/Event.java
@@ -13,7 +13,7 @@ import java.util.List;
@Setter
@NoArgsConstructor
@AllArgsConstructor
-@Builder
+@RequiredArgsConstructor
public class Event {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -21,7 +21,7 @@ public class Event {
@Column(nullable = false)
@NonNull
- private String title;
+ private String name;
@Column(nullable = false)
@NonNull
@@ -35,7 +35,7 @@ public class Event {
@NonNull
private Date end;
- @OneToMany(mappedBy = "event")
+ @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true)
@Column(nullable = false)
private List categories = new ArrayList<>();
diff --git a/src/main/java/me/jweissen/aeticket/model/User.java b/src/main/java/me/jweissen/aeticket/model/User.java
index 6c41e4c..c068f7d 100644
--- a/src/main/java/me/jweissen/aeticket/model/User.java
+++ b/src/main/java/me/jweissen/aeticket/model/User.java
@@ -3,6 +3,7 @@ package me.jweissen.aeticket.model;
import jakarta.persistence.*;
import lombok.*;
+import java.util.Date;
import java.util.List;
@Entity
@@ -35,11 +36,19 @@ public class User {
@Column
private String token;
+ @Column
+ private Date tokenValidUntil;
+
@Column(nullable = false)
@Enumerated(EnumType.ORDINAL)
@NonNull
private Role role;
+ @OneToOne
+ @JoinColumn(nullable = false)
+ @NonNull
+ private Cart currentCart;
+
@OneToMany(mappedBy = "user")
private List carts;
}
\ No newline at end of file
diff --git a/src/main/java/me/jweissen/aeticket/repository/CartRepository.java b/src/main/java/me/jweissen/aeticket/repository/CartRepository.java
index 155d98b..316461c 100644
--- a/src/main/java/me/jweissen/aeticket/repository/CartRepository.java
+++ b/src/main/java/me/jweissen/aeticket/repository/CartRepository.java
@@ -2,6 +2,10 @@ package me.jweissen.aeticket.repository;
import me.jweissen.aeticket.model.Cart;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
public interface CartRepository extends JpaRepository {
+ @Query("SELECT sum(ce.amount * c.price) FROM CartEntry ce INNER JOIN Category c ON ce.category = c WHERE ce.cart = :cart")
+ Integer getCheckoutPrice(@Param("cart") Cart cart);
}
diff --git a/src/main/java/me/jweissen/aeticket/repository/CategoryRepository.java b/src/main/java/me/jweissen/aeticket/repository/CategoryRepository.java
index 9cca126..0328402 100644
--- a/src/main/java/me/jweissen/aeticket/repository/CategoryRepository.java
+++ b/src/main/java/me/jweissen/aeticket/repository/CategoryRepository.java
@@ -2,6 +2,10 @@ package me.jweissen.aeticket.repository;
import me.jweissen.aeticket.model.Category;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
public interface CategoryRepository extends JpaRepository {
+ @Query("SELECT c.stock - sum(ce.amount) FROM Category c INNER JOIN CartEntry ce ON c = ce.category WHERE c = :category GROUP BY c")
+ Integer availableTickets(@Param("category") Category category);
}
diff --git a/src/main/java/me/jweissen/aeticket/repository/UserRepository.java b/src/main/java/me/jweissen/aeticket/repository/UserRepository.java
index 54a309f..e398393 100644
--- a/src/main/java/me/jweissen/aeticket/repository/UserRepository.java
+++ b/src/main/java/me/jweissen/aeticket/repository/UserRepository.java
@@ -3,7 +3,7 @@ package me.jweissen.aeticket.repository;
import me.jweissen.aeticket.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
-public interface UserRepository extends JpaRepository {
+public interface UserRepository extends JpaRepository {
User findByToken(String token);
User findByEmail(String email);
}
diff --git a/src/main/java/me/jweissen/aeticket/service/AuthService.java b/src/main/java/me/jweissen/aeticket/service/AuthService.java
index 4958454..ba17523 100644
--- a/src/main/java/me/jweissen/aeticket/service/AuthService.java
+++ b/src/main/java/me/jweissen/aeticket/service/AuthService.java
@@ -1,29 +1,56 @@
package me.jweissen.aeticket.service;
+import lombok.Getter;
import me.jweissen.aeticket.model.Role;
import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
+import java.util.Date;
import java.util.Optional;
@Service
public class AuthService {
private final JwtService jwtService;
private final UserRepository userRepository;
+ private final Long tokenValidForMillis;
+ @Getter
+ private User currentUser;
- public AuthService(JwtService jwtService, UserRepository userRepository) {
+ public AuthService(
+ JwtService jwtService,
+ UserRepository userRepository,
+ @Value("${token.validForHours}") Long tokenValidForHours) {
+ this.tokenValidForMillis = 1000L * 3600 * tokenValidForHours;
this.jwtService = jwtService;
this.userRepository = userRepository;
}
- public Optional getRole(String token) {
- User user = userRepository.findByToken(token);
- if (user == null) {
- return Optional.empty();
- }
- return Optional.of(user.getRole());
+ public void extendToken(User user) {
+ user.setTokenValidUntil(new Date(System.currentTimeMillis() + tokenValidForMillis));
+ userRepository.save(user);
}
-
+ public Optional authenticate(String token) {
+ Optional userIdOptional = jwtService.getUserId(token);
+ // token not valid or userId
+ if (userIdOptional.isEmpty()) {
+ return Optional.empty();
+ }
+ Optional userOptional = userRepository.findById(userIdOptional.get());
+ if (userOptional.isEmpty()) {
+ // user with id not found
+ return Optional.empty();
+ }
+ User user = userOptional.get();
+ if (user.getTokenValidUntil().before(new Date())) {
+ // token expired
+ return Optional.empty();
+ }
+ // success
+ extendToken(user);
+ currentUser = user;
+ return Optional.of(user);
+ }
}
diff --git a/src/main/java/me/jweissen/aeticket/service/CartService.java b/src/main/java/me/jweissen/aeticket/service/CartService.java
index eb18f7a..fff2d27 100644
--- a/src/main/java/me/jweissen/aeticket/service/CartService.java
+++ b/src/main/java/me/jweissen/aeticket/service/CartService.java
@@ -1,23 +1,36 @@
package me.jweissen.aeticket.service;
-import me.jweissen.aeticket.dto.response.CartEntryResponseDto;
+import me.jweissen.aeticket.dto.request.CartAddRequestDto;
+import me.jweissen.aeticket.dto.request.CartEntryRequestDto;
import me.jweissen.aeticket.dto.response.CartEventResponseDto;
+import me.jweissen.aeticket.dto.response.CheckoutResponseDto;
import me.jweissen.aeticket.model.Cart;
+import me.jweissen.aeticket.model.CartEntry;
+import me.jweissen.aeticket.model.Category;
import me.jweissen.aeticket.model.Event;
import me.jweissen.aeticket.repository.CartEntryRepository;
import me.jweissen.aeticket.repository.CartRepository;
+import me.jweissen.aeticket.repository.CategoryRepository;
+import me.jweissen.aeticket.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.List;
+import java.util.Optional;
@Service
public class CartService {
private final CartRepository cartRepository;
private final CartEntryRepository cartEntryRepository;
+ private final CategoryRepository categoryRepository;
+ private final UserRepository userRepository;
- public CartService(CartRepository cartRepository, CartEntryRepository cartEntryRepository) {
+ public CartService(CartRepository cartRepository, CartEntryRepository cartEntryRepository,
+ CategoryRepository categoryRepository,
+ UserRepository userRepository) {
this.cartRepository = cartRepository;
this.cartEntryRepository = cartEntryRepository;
+ this.categoryRepository = categoryRepository;
+ this.userRepository = userRepository;
}
public List toDto(Cart cart) {
@@ -27,7 +40,7 @@ public class CartService {
return distinctEvents.stream().map(event ->
new CartEventResponseDto(
event.getId(),
- event.getTitle(),
+ event.getName(),
event.getStart(),
event.getEnd(),
event.getDescription(),
@@ -35,6 +48,35 @@ public class CartService {
)).toList();
}
- //public List getCartByAuthToken() {
+ public boolean add(CartAddRequestDto dto, Cart cart) {
+ // posting the eventIds is redundant so, those are ignored
+ for (CartEntryRequestDto cartEntryDto: dto.cartEntries()) {
+ Optional categoryOptional = categoryRepository.findById(cartEntryDto.id());
+ if (categoryOptional.isEmpty()) {
+ // category id not found
+ return false;
+ }
+ Category category = categoryOptional.get();
+ if (cartEntryDto.amount() > categoryRepository.availableTickets(category)) {
+ // wants to order more tickets than available
+ return false;
+ }
+ cart.getCartEntries().add(new CartEntry(cart, category, cartEntryDto.amount()));
+ }
+ cartRepository.save(cart);
+ return true;
+ }
+ public CheckoutResponseDto checkout(Cart cart) {
+ cart.setCheckedOut(true);
+ cartRepository.save(cart);
+ // reset current cart
+ Cart newCart = new Cart();
+ newCart = cartRepository.save(newCart);
+ cart.getUser().setCurrentCart(newCart);
+ userRepository.save(cart.getUser());
+ return new CheckoutResponseDto(
+ CategoryService.centsToEuros(cartRepository.getCheckoutPrice(cart))
+ );
+ }
}
diff --git a/src/main/java/me/jweissen/aeticket/service/CategoryService.java b/src/main/java/me/jweissen/aeticket/service/CategoryService.java
index 594f9a2..8006854 100644
--- a/src/main/java/me/jweissen/aeticket/service/CategoryService.java
+++ b/src/main/java/me/jweissen/aeticket/service/CategoryService.java
@@ -27,6 +27,10 @@ public class CategoryService {
);
}
+ public static List fromDtos(List dtos) {
+ return dtos.stream().map(CategoryService::fromDto).toList();
+ }
+
public static CategoryResponseDto toDto(Category category) {
return new CategoryResponseDto(
category.getId(),
@@ -54,6 +58,7 @@ public class CategoryService {
}
public boolean update(CategoryUpdateRequestDto dto) {
+ System.out.println(dto);
return categoryRepository.findById(dto.id())
.map(category -> {
category.setName(dto.name());
diff --git a/src/main/java/me/jweissen/aeticket/service/EventService.java b/src/main/java/me/jweissen/aeticket/service/EventService.java
index 7684a0c..0d0da3b 100644
--- a/src/main/java/me/jweissen/aeticket/service/EventService.java
+++ b/src/main/java/me/jweissen/aeticket/service/EventService.java
@@ -3,7 +3,9 @@ package me.jweissen.aeticket.service;
import me.jweissen.aeticket.dto.request.EventRequestDto;
import me.jweissen.aeticket.dto.request.EventUpdateRequestDto;
import me.jweissen.aeticket.dto.response.EventResponseDto;
+import me.jweissen.aeticket.model.Category;
import me.jweissen.aeticket.model.Event;
+import me.jweissen.aeticket.repository.CategoryRepository;
import me.jweissen.aeticket.repository.EventRepository;
import org.springframework.stereotype.Service;
@@ -13,24 +15,27 @@ import java.util.Optional;
@Service
public class EventService {
private final EventRepository eventRepository;
+ private final CategoryRepository categoryRepository;
- public EventService(EventRepository eventRepository) {
+ public EventService(EventRepository eventRepository,
+ CategoryRepository categoryRepository) {
this.eventRepository = eventRepository;
+ this.categoryRepository = categoryRepository;
}
public static Event fromDto(EventRequestDto dto) {
- return Event.builder()
- .title(dto.name())
- .description(dto.description())
- .start(dto.from())
- .end(dto.to())
- .build();
+ return new Event(
+ dto.name(),
+ dto.description(),
+ dto.from(),
+ dto.to()
+ );
}
public static EventResponseDto toDto(Event event) {
return new EventResponseDto(
event.getId(),
- event.getTitle(),
+ event.getName(),
event.getStart(),
event.getEnd(),
event.getDescription(),
@@ -42,14 +47,23 @@ public class EventService {
return events.stream().map(EventService::toDto).toList();
}
- public void create(EventRequestDto event) {
- eventRepository.save(EventService.fromDto(event));
+ public void create(EventRequestDto dto) {
+ Event event = EventService.fromDto(dto);
+ event = eventRepository.save(event);
+ Event finalEvent = event;
+ List categories = CategoryService.fromDtos(dto.ticketCategories());
+ categories.forEach(c -> c.setEvent(finalEvent));
+ categoryRepository.saveAll(categories);
}
public boolean update(EventUpdateRequestDto dto) {
return eventRepository.findById(dto.id())
.map(event -> {
- // dto auf object assignen
+ event.setName(dto.name());
+ event.setDescription(dto.description());
+ event.setStart(dto.from());
+ event.setEnd(dto.to());
+ eventRepository.save(event);
return true;
})
.orElse(false);
diff --git a/src/main/java/me/jweissen/aeticket/service/JwtService.java b/src/main/java/me/jweissen/aeticket/service/JwtService.java
index 076b8e7..574d1ad 100644
--- a/src/main/java/me/jweissen/aeticket/service/JwtService.java
+++ b/src/main/java/me/jweissen/aeticket/service/JwtService.java
@@ -6,6 +6,8 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
+import me.jweissen.aeticket.model.User;
+import me.jweissen.aeticket.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -17,10 +19,8 @@ public class JwtService {
private final JWTVerifier jwtVerifier;
private final Algorithm algorithm;
private final String userIdClaimKey = "userId";
- private final Long tokenValidForMillis;
- public JwtService(@Value("${token.secret}") String secret, @Value("${token.validForHours}") Long tokenValidForHours) {
- tokenValidForMillis = 1000L * 3600 * tokenValidForHours;
+ public JwtService(@Value("${token.secret}") String secret) {
algorithm = Algorithm.HMAC256(secret);
jwtVerifier = JWT.require(algorithm).build();
}
@@ -29,8 +29,6 @@ public class JwtService {
return JWT.create()
.withSubject("aeticket user token")
.withClaim(userIdClaimKey, userId)
- .withIssuedAt(new Date())
- .withExpiresAt(new Date(System.currentTimeMillis() + tokenValidForMillis))
.sign(algorithm);
}
@@ -42,11 +40,16 @@ public class JwtService {
// token not valid
return Optional.empty();
}
- // token expired
- if (decodedJWT.getExpiresAt().before(new Date())) {
+ Claim userIdClaim = decodedJWT.getClaim(userIdClaimKey);
+ if (userIdClaim.isNull()) {
+ // userId claim not present
return Optional.empty();
}
- Claim claim = decodedJWT.getClaim(userIdClaimKey);
- return Optional.of(claim.asLong());
+ Long userId = userIdClaim.asLong();
+ if (userId == null) {
+ // userId claim not a long
+ return Optional.empty();
+ }
+ return Optional.of(userId);
}
}
diff --git a/src/main/java/me/jweissen/aeticket/service/UserService.java b/src/main/java/me/jweissen/aeticket/service/UserService.java
index e0bf4d9..c05d1d2 100644
--- a/src/main/java/me/jweissen/aeticket/service/UserService.java
+++ b/src/main/java/me/jweissen/aeticket/service/UserService.java
@@ -5,6 +5,7 @@ import me.jweissen.aeticket.dto.request.SignupRequestDto;
import me.jweissen.aeticket.dto.request.UserUpdateRequestDto;
import me.jweissen.aeticket.dto.response.TokenResponseDto;
import me.jweissen.aeticket.dto.response.UserResponseDto;
+import me.jweissen.aeticket.model.Cart;
import me.jweissen.aeticket.model.Role;
import me.jweissen.aeticket.model.User;
import me.jweissen.aeticket.repository.UserRepository;
@@ -38,7 +39,8 @@ public class UserService {
dto.lastname(),
dto.email(),
dto.password(),
- Role.USER
+ Role.USER,
+ new Cart()
);
}
@@ -62,7 +64,7 @@ public class UserService {
return new TokenResponseDto(generateToken(user));
}
- public void delete(Integer id) {
+ public void delete(Long id) {
userRepository.deleteById(id);
}
@@ -87,7 +89,7 @@ public class UserService {
return true;
}
- public Optional getById(Integer id) {
+ public Optional getById(Long id) {
return userRepository.findById(id).map(UserService::toDto);
}
}