Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,39 @@
# shopserver
## Sample REST API for ecommerce shop

---
#### Technology Stack:
* Java 21
* Spring Boot 3.5.0
* Spring MVC
* Spring Data JPA
* Spring Security
* Lombok 1.18.36
* Flyway 11.9.0
* JWT 0.12.6
* Hibernate Validator 8.0.1
* (Testing) H2 database 2.3.232
* (Testing) Spring Security Test

#### Basic functionalities:
* Creating and managing products
* Creating and managing product categories
* Authorize safely using JWT
* Creating and managing users
* Placing and managing orders and payments

To be done:
* Adding Swagger
* more functionalities...
* more improvements...

---
#### [DEV]
To make it work go to Edit Configurations/Edit Configuration Templates/JUnit and add:
1. `-Dspring.profiles.active=test` in the VM options
2. In the environment variables:
1. `LOG_LEVEL=DEBUG` - or any other
2. `COOKIE_NAME=dgshopserver` - name of the cookie
3. `JWT_SECRET=` String which is your secret to compute HMAC SHA key for JWT
4. `JWT_TTL_HOURS=` number of hours for how long the JWT token is valid

NOTE: all the environment variables must be separated by semicolon `;` in one line
40 changes: 40 additions & 0 deletions src/main/java/dg/shop/server/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dg.shop.server.controller;

import dg.shop.server.payload.order.OrderDTO;
import dg.shop.server.payload.order.OrderRequestDTO;
import dg.shop.server.service.order.OrderService;
import dg.shop.server.util.AuthUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {

private OrderService orderService;

private AuthUtils authUtils;

public OrderController(OrderService orderService, AuthUtils authUtils) {
this.orderService = orderService;
this.authUtils = authUtils;
}

@PostMapping("/api/order/users/payments/{paymentMethod}")
public ResponseEntity<OrderDTO> orderProducts(@PathVariable String paymentMethod, @RequestBody OrderRequestDTO orderRequestDTO) {
String email = authUtils.signedInEmail();
OrderDTO order = orderService.placeOrder(
email,
orderRequestDTO.addressId(),
paymentMethod,
orderRequestDTO.paymentGatewayName(),
orderRequestDTO.paymentGatewayPaymentId(),
orderRequestDTO.paymentGatewayStatus(),
orderRequestDTO.paymentGatewayResponseMessage()
);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
}
8 changes: 0 additions & 8 deletions src/main/java/dg/shop/server/model/Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,12 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Transient;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.ArrayList;
import java.util.List;

@Data
@Entity
Expand Down
9 changes: 0 additions & 9 deletions src/main/java/dg/shop/server/model/Cart.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
package dg.shop.server.model;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Entity
@Data
@Table(name = "carts")
Expand All @@ -30,15 +25,11 @@ public class Cart {
@JoinColumn(name = "user_id")
private User user;

@OneToMany(mappedBy = "cart", cascade = { CascadeType.ALL }, orphanRemoval = true)
private List<CartItem> cartItems = new ArrayList<>();

private double totalPrice = 0.0d;

@Override
public String toString() {
return "Cart{" +
"cartItems size=" + cartItems.size() +
", cartId=" + cartId +
", user=" + user +
", totalPrice=" + totalPrice +
Expand Down
13 changes: 0 additions & 13 deletions src/main/java/dg/shop/server/model/Category.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package dg.shop.server.model;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.List;

@Data
@NoArgsConstructor
Expand All @@ -26,12 +21,4 @@ public class Category {

@Column
private String name;

@ToString.Exclude
@OneToMany(mappedBy = "category", cascade = { CascadeType.ALL })
private List<Product> products;

public Category(long id, String name) {
this(id, name, List.of());
}
}
59 changes: 59 additions & 0 deletions src/main/java/dg/shop/server/model/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package dg.shop.server.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Email;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Entity
@Data
@Table(name = "orders")
@NoArgsConstructor
@AllArgsConstructor
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long orderId;

@Email
@Column(nullable = false)
private String email;

private LocalDate orderDate;

@OneToOne
@JoinColumn(name = "payment_id")
private Payment payment;

private double totalAmount;

private String orderStatus;

@ManyToOne
@JoinColumn(name = "address_id")
private Address address;

@Override
public String toString() {
return "Order{" +
"address id=" + address.getAddressId() +
", orderId=" + orderId +
", email='" + email + '\'' +
", orderDate=" + orderDate +
", payment id=" + payment.getPaymentId() +
", totalAmount=" + totalAmount +
", orderStatus='" + orderStatus + '\'' +
'}';
}
}
49 changes: 49 additions & 0 deletions src/main/java/dg/shop/server/model/OrderItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dg.shop.server.model;

import jakarta.persistence.Entity;
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.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@Table(name = "order_items")
@NoArgsConstructor
@AllArgsConstructor
public class OrderItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long orderItemId;

@ManyToOne
@JoinColumn(name = "product_id")
private Product product;

@ManyToOne
@JoinColumn(name = "order_id")
private Order order;

private int quantity;

private double discount;

private double orderedProductPrice;

@Override
public String toString() {
return "OrderItem{" +
"discount=" + discount +
", orderItemId=" + orderItemId +
", product id=" + product.getId() +
", order id=" + order.getOrderId() +
", quantity=" + quantity +
", orderedProductPrice=" + orderedProductPrice +
'}';
}
}
53 changes: 53 additions & 0 deletions src/main/java/dg/shop/server/model/Payment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package dg.shop.server.model;

import dg.shop.server.validation.RequiredSize;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@Table(name = "payments")
@NoArgsConstructor
@AllArgsConstructor
public class Payment {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long paymentId;

@NotBlank
@RequiredSize(min = 4, sizeMessage = "Payment method must contain at least 4 characters")
private String paymentMethod;

private String paymentGatewayPaymentId;
private String paymentGatewayStatus;
private String paymentGatewayResponseMessage;
private String paymentGatewayName;

public Payment(String paymentMethod, String paymentGatewayPaymentId, String paymentGatewayStatus, String paymentGatewayResponseMessage, String paymentGatewayName) {
this.paymentMethod = paymentMethod;
this.paymentGatewayPaymentId = paymentGatewayPaymentId;
this.paymentGatewayStatus = paymentGatewayStatus;
this.paymentGatewayResponseMessage = paymentGatewayResponseMessage;
this.paymentGatewayName = paymentGatewayName;
}

@Override
public String toString() {
return "Payment{" +
", paymentId=" + paymentId +
", paymentMethod='" + paymentMethod + '\'' +
", paymentGatewayPaymentId='" + paymentGatewayPaymentId + '\'' +
", paymentGatewayStatus='" + paymentGatewayStatus + '\'' +
", paymentGatewayResponseMessage='" + paymentGatewayResponseMessage + '\'' +
", paymentGatewayName='" + paymentGatewayName + '\'' +
'}';
}
}
5 changes: 1 addition & 4 deletions src/main/java/dg/shop/server/model/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.Cascade;

import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -44,9 +45,6 @@ public class Product {
@JoinColumn(name = "seller_id")
private User seller;

@OneToMany(mappedBy = "product", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
private List<CartItem> products = new ArrayList<>();

@Override
public String toString() {
return "Product{" +
Expand All @@ -60,7 +58,6 @@ public String toString() {
", discount=" + discount +
", specialPrice=" + specialPrice +
", seller=" + seller +
", products=" + products.size() +
'}';
}
}
Loading
Loading