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
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package mate.academy.bookshop.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import mate.academy.bookshop.dto.cartitem.CartItemRequestDto;
import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto;
import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto;
import mate.academy.bookshop.model.User;
import mate.academy.bookshop.service.shoppingcart.ShoppingCartService;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
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.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@Tag(name = "Shopping Cart",
description = "Operations for managing shopping cart")
@RequestMapping("/cart")
public class ShoppingCartController {
private final ShoppingCartService shoppingCartService;

@Operation(summary = "Get the authenticated user's shopping cart",
description = "Retrieves the shopping cart of the currently authenticated user.")
@GetMapping
public ShoppingCartResponseDto getShoppingCart(Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return shoppingCartService.getShoppingCart(authenticatedUserId);
}

@Operation(summary = "Add an item to the shopping cart",
description = "Adds a new item to the shopping cart for the authenticated user.")
@PostMapping
public ShoppingCartResponseDto addCartItem(
@RequestBody @Valid CartItemRequestDto cartItemRequestDto,
Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return shoppingCartService.addCartItem(cartItemRequestDto, authenticatedUserId);
}

@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Update an item in the shopping cart",
description = "Updates the details of a specific item"
+ " in the shopping cart for the authenticated user.")
@PutMapping("/cart-items/{cartItemId}")
public ShoppingCartResponseDto updateCartItems(
@RequestBody @Valid CartItemUpdateDto cartItemUpdateDto,
@PathVariable Long cartItemId,
Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
return shoppingCartService.updateCartItems(
authenticatedUserId,
cartItemId,
cartItemUpdateDto
);
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(summary = "Delete an item from the shopping cart",
description = "Removes a specific item from the shopping cart"
+ " for the authenticated user.")
@DeleteMapping("/cart-items/{cartItemId}")
public void deleteCartItem(@PathVariable Long cartItemId, Authentication authentication) {
Long authenticatedUserId = getAuthenticatedUserId(authentication);
shoppingCartService.deleteCartItemFromCart(authenticatedUserId, cartItemId);
}

private Long getAuthenticatedUserId(Authentication authentication) {
return ((User) authentication.getPrincipal()).getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mate.academy.bookshop.dto.cartitem;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Data;

@Data
public class CartItemRequestDto {
@NotNull

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@NotNull
@NotNull
@Positive

@Positive
private Long bookId;

@Positive
private int quantity;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mate.academy.bookshop.dto.cartitem;

import lombok.Data;

@Data
public class CartItemResponseDto {
private Long id;

private Long bookId;

private String bookTitle;

private int quantity;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package mate.academy.bookshop.dto.cartitem;

import jakarta.validation.constraints.Positive;

public record CartItemUpdateDto(@Positive int quantity) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mate.academy.bookshop.dto.shoppingcart;

import java.util.Set;
import lombok.Data;
import mate.academy.bookshop.dto.cartitem.CartItemResponseDto;

@Data
public class ShoppingCartResponseDto {
private Long id;

private Long userId;

private Set<CartItemResponseDto> cartItemsDto;
}
9 changes: 9 additions & 0 deletions src/main/java/mate/academy/bookshop/mapper/BookMapper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mate.academy.bookshop.mapper;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import mate.academy.bookshop.config.MapperConfig;
Expand All @@ -13,6 +14,7 @@
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;
import org.mapstruct.NullValuePropertyMappingStrategy;

@Mapper(config = MapperConfig.class)
Expand Down Expand Up @@ -47,4 +49,11 @@ default Set<Category> mapCategoryIdsToCategories(List<Long> categoriesIds) {
.map(Category::new)
.collect(Collectors.toSet());
}

@Named("bookById")
default Book bookById(Long id) {
return Optional.ofNullable(id)
.map(Book::new)
.orElse(null);
}
}
26 changes: 26 additions & 0 deletions src/main/java/mate/academy/bookshop/mapper/CartItemMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package mate.academy.bookshop.mapper;

import mate.academy.bookshop.config.MapperConfig;
import mate.academy.bookshop.dto.cartitem.CartItemRequestDto;
import mate.academy.bookshop.dto.cartitem.CartItemResponseDto;
import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto;
import mate.academy.bookshop.model.CartItem;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Named;

@Mapper(config = MapperConfig.class, uses = BookMapper.class)
public interface CartItemMapper {
@Named("cartItemToDto")
@Mapping(source = "book.id", target = "bookId")
@Mapping(source = "book.title", target = "bookTitle")
CartItemResponseDto toDto(CartItem cartItem);

@Mapping(target = "book", source = "bookId", qualifiedByName = "bookById")
@Mapping(target = "shoppingCart", ignore = true)
CartItem toEntity(CartItemRequestDto cartItemRequestDto);

void updateCartItemFromDto(CartItemUpdateDto cartItemUpdateDto,
@MappingTarget CartItem cartItem);
}
15 changes: 15 additions & 0 deletions src/main/java/mate/academy/bookshop/mapper/ShoppingCartMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mate.academy.bookshop.mapper;

import mate.academy.bookshop.config.MapperConfig;
import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto;
import mate.academy.bookshop.model.ShoppingCart;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(config = MapperConfig.class, uses = CartItemMapper.class)
public interface ShoppingCartMapper {
@Mapping(source = "user.id", target = "userId")
@Mapping(source = "cartItems", target = "cartItemsDto", qualifiedByName = "cartItemToDto")
ShoppingCartResponseDto toDto(ShoppingCart shoppingCart);

}
6 changes: 6 additions & 0 deletions src/main/java/mate/academy/bookshop/model/Book.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Set;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.SQLDelete;
Expand All @@ -24,6 +25,7 @@
@Setter
@SQLDelete(sql = "UPDATE books SET is_deleted = true WHERE id=?")
@SQLRestriction(value = "is_deleted = false")
@NoArgsConstructor
@Table(name = "books")
public class Book {
@Id
Expand Down Expand Up @@ -59,4 +61,8 @@ public class Book {
@ToString.Exclude
@EqualsAndHashCode.Exclude
private Set<Category> categories = new HashSet<>();

public Book(Long id) {
this.id = id;
}
}
34 changes: 34 additions & 0 deletions src/main/java/mate/academy/bookshop/model/CartItem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package mate.academy.bookshop.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
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.Setter;

@Entity
@Setter
@Getter
@Table(name = "cart_items")
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "cart_id", nullable = false)
private ShoppingCart shoppingCart;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "book_id", nullable = false)
private Book book;

@Column(nullable = false)
private int quantity;
}
38 changes: 38 additions & 0 deletions src/main/java/mate/academy/bookshop/model/ShoppingCart.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package mate.academy.bookshop.model;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Entity
@Setter
@Getter
@SQLDelete(sql = "UPDATE shopping_carts SET is_deleted = true WHERE id=?")
@SQLRestriction(value = "is_deleted = false")
@Table(name = "shopping_carts")
public class ShoppingCart {
@Id
private Long id;

@MapsId
@OneToOne(fetch = FetchType.LAZY, optional = false)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use @MapsId

@JoinColumn(name = "user_id", unique = true)
private User user;

@OneToMany(mappedBy = "shoppingCart", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<CartItem> cartItems;

private boolean isDeleted = false;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package mate.academy.bookshop.repository;

import java.util.Optional;
import mate.academy.bookshop.model.CartItem;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CartItemRepository extends JpaRepository<CartItem, Long> {
Optional<CartItem> findByIdAndShoppingCartId(Long itemId, Long cartId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.bookshop.repository;

import java.util.Optional;
import mate.academy.bookshop.model.ShoppingCart;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ShoppingCartRepository extends JpaRepository<ShoppingCart, Long> {
@EntityGraph(attributePaths = {"cartItems", "cartItems.book"})
Optional<ShoppingCart> findByUserId(Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package mate.academy.bookshop.service.shoppingcart;

import mate.academy.bookshop.dto.cartitem.CartItemRequestDto;
import mate.academy.bookshop.dto.cartitem.CartItemUpdateDto;
import mate.academy.bookshop.dto.shoppingcart.ShoppingCartResponseDto;
import mate.academy.bookshop.model.User;

public interface ShoppingCartService {
void createShoppingCart(User user);

ShoppingCartResponseDto getShoppingCart(Long userId);

ShoppingCartResponseDto addCartItem(CartItemRequestDto cartItemUpdateDto, Long userId);

ShoppingCartResponseDto updateCartItems(Long userId,
Long cartItemId,
CartItemUpdateDto cartItemUpdateDto
);

void deleteCartItemFromCart(Long userId, Long cartItemId);
}
Loading