beidl message
This commit is contained in:
parent
fc372073f3
commit
85d7162419
28 changed files with 413 additions and 84 deletions
21
http-requests/cart.http
Normal file
21
http-requests/cart.http
Normal file
|
|
@ -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}}
|
||||
34
http-requests/category.http
Normal file
34
http-requests/category.http
Normal file
|
|
@ -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}}
|
||||
53
http-requests/event.http
Normal file
53
http-requests/event.http
Normal file
|
|
@ -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}}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"dev": {
|
||||
"url": "http://localhost:8080/api/v1"
|
||||
"url": "http://localhost:8080/api/v1",
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZXRpY2tldCB1c2VyIHRva2VuIiwiZXhwIjoxNzAzMTAwNzQyLCJ1c2VySWQiOjIsImlhdCI6MTcwMzAxNDM0Mn0.D9HBxWy1vIP82XOh_ocjLO9HB0lK_rQGjgD3a7KQrOE"
|
||||
}
|
||||
}
|
||||
|
|
@ -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}}
|
||||
|
|
|
|||
4
pom.xml
4
pom.xml
|
|
@ -25,6 +25,10 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
|
|
|
|||
11
src/main/java/me/jweissen/aeticket/aspect/AdminOnly.java
Normal file
11
src/main/java/me/jweissen/aeticket/aspect/AdminOnly.java
Normal file
|
|
@ -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 {
|
||||
}
|
||||
63
src/main/java/me/jweissen/aeticket/aspect/AuthAspect.java
Normal file
63
src/main/java/me/jweissen/aeticket/aspect/AuthAspect.java
Normal file
|
|
@ -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<User> 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> 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> 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();
|
||||
}
|
||||
}
|
||||
11
src/main/java/me/jweissen/aeticket/aspect/UserOnly.java
Normal file
11
src/main/java/me/jweissen/aeticket/aspect/UserOnly.java
Normal file
|
|
@ -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 {
|
||||
}
|
||||
|
|
@ -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<Void> addEntry(CartAddRequestDto dto) {
|
||||
// TODO
|
||||
return ResponseEntity.noContent().build();
|
||||
public ResponseEntity<Void> 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<List<CartEntryResponseDto>> getCartEntries() {
|
||||
// TODO
|
||||
return new ResponseEntity<>(null, HttpStatus.OK);
|
||||
public ResponseEntity<List<CartEventResponseDto>> getCartEntries() {
|
||||
return new ResponseEntity<>(cartService.toDto(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/checkout")
|
||||
public ResponseEntity<CheckoutResponseDto> checkout() {
|
||||
// TODO
|
||||
return ResponseEntity.badRequest().build();
|
||||
return new ResponseEntity<>(cartService.checkout(authService.getCurrentUser().getCurrentCart()), HttpStatus.OK);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Void> 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<Void> 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<Void> 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<CategoryResponseDto> 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<List<CategoryResponseDto>> getAll() {
|
||||
return new ResponseEntity<>(categoryService.getAll(), HttpStatus.OK);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,28 +23,28 @@ public class EventController {
|
|||
public ResponseEntity<Void> 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<Void> 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<Void> 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<EventResponseDto> 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")
|
||||
|
|
|
|||
|
|
@ -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<Void> 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<Void> delete(@PathVariable Integer id) {
|
||||
public ResponseEntity<Void> 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<List<UserResponseDto>> getAll() {
|
||||
// TODO admin only
|
||||
return new ResponseEntity<>(userService.getAll(), HttpStatus.OK);
|
||||
}
|
||||
|
||||
@GetMapping("/load/{id}")
|
||||
public ResponseEntity<UserResponseDto> getById(@PathVariable Integer id) {
|
||||
public ResponseEntity<UserResponseDto> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<CartEntry> cartEntries;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Category> categories = new ArrayList<>();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Cart> carts;
|
||||
}
|
||||
|
|
@ -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<Cart, Integer> {
|
||||
@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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Category, Long> {
|
||||
@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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<User, Integer> {
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
User findByToken(String token);
|
||||
User findByEmail(String email);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Role> 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<User> authenticate(String token) {
|
||||
Optional<Long> userIdOptional = jwtService.getUserId(token);
|
||||
// token not valid or userId
|
||||
if (userIdOptional.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Optional<User> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<CartEventResponseDto> 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<CartEntryResponseDto> getCartByAuthToken() {
|
||||
public boolean add(CartAddRequestDto dto, Cart cart) {
|
||||
// posting the eventIds is redundant so, those are ignored
|
||||
for (CartEntryRequestDto cartEntryDto: dto.cartEntries()) {
|
||||
Optional<Category> 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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ public class CategoryService {
|
|||
);
|
||||
}
|
||||
|
||||
public static List<Category> fromDtos(List<CategoryRequestDto> 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());
|
||||
|
|
|
|||
|
|
@ -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<Category> 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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<UserResponseDto> getById(Integer id) {
|
||||
public Optional<UserResponseDto> getById(Long id) {
|
||||
return userRepository.findById(id).map(UserService::toDto);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue