Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Docker support #15

Merged
merged 6 commits into from
May 27, 2024
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ build/
!**/src/test/**/build/

### VS Code ###
.vscode/
.vscode/

### Docker ###
/.env
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Builder stage
FROM openjdk:21-jdk-slim as builder
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

# Final stage
FROM openjdk:21-jdk-slim
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
EXPOSE 8080
34 changes: 34 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
version: "3.8"

services:
mysqldb:
image: mysql:8
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
ports:
- "${MYSQL_LOCAL_PORT}:${MYSQL_DOCKER_PORT}"
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -P ${MYSQL_DOCKER_PORT} -u ${MYSQL_USER} -p${MYSQL_ROOT_PASSWORD}"]
interval: 30s
timeout: 10s
retries: 3

app:
depends_on:
mysqldb:
condition: service_healthy
image: book-service
build: .
ports:
- "${SPRING_LOCAL_PORT}:${SPRING_DOCKER_PORT}"
- "${DEBUG_PORT}:${DEBUG_PORT}"
environment:
SPRING_APPLICATION_JSON: '{
"spring.datasource.url": "jdbc:mysql://mysqldb:${MYSQL_DOCKER_PORT}/${MYSQL_DATABASE}?serverTimezone=UTC",
"spring.datasource.username": "${MYSQL_USER}",
"spring.datasource.password": "${MYSQL_ROOT_PASSWORD}",
"jwt.secret": "${JWT_SECRET}"
}'
JAVA_TOOL_OPTIONS: "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
5 changes: 4 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package mate.academy.bookstore.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.bookstore.dto.order.OrderItemResponseDto;
import mate.academy.bookstore.dto.order.OrderRequestDto;
import mate.academy.bookstore.dto.order.OrderResponseDto;
import mate.academy.bookstore.dto.order.OrderStatusDto;
import mate.academy.bookstore.model.User;
import mate.academy.bookstore.service.OrderService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Order Management", description = "Endpoints for managing the order")
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;

@PostMapping
@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_ADMIN')")
@Operation(summary = "Create an order",
description = "Creates a new order with the items in the current user cart")
public void createOrder(
@Valid @RequestBody OrderRequestDto orderDto,
@AuthenticationPrincipal User user) {
orderService.createOrder(orderDto, user);
}

@GetMapping
@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_ADMIN')")
@Operation(summary = "Get orders history",
description = "Retrieves the order history for the current user")
public List<OrderResponseDto> getOrdersHistory(
@AuthenticationPrincipal User currentUser) {
return orderService.getAllOrders(currentUser);
}

@PatchMapping("/{orderId}")
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@Operation(summary = "Update an order status",
description = "Updates the status of an existing order identified by its id")
public void updateOrderStatus(
@Valid @RequestBody OrderStatusDto statusDto,
@PathVariable Long orderId,
@AuthenticationPrincipal User user) {
orderService.updateStatus(statusDto, orderId, user.getId());
}

@GetMapping("/{orderId}/items")
@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_ADMIN')")
@Operation(summary = "Get all order items from order",
description = "Retrieves all items associated for authenticated user")
public List<OrderItemResponseDto> getAllOrderItems(
@PathVariable Long orderId,
@AuthenticationPrincipal User user) {
return orderService.getAllOrderItems(orderId, user);
}

@GetMapping("/{orderId}/items/{itemId}")
@PreAuthorize("hasAnyRole('ROLE_USER','ROLE_ADMIN')")
@Operation(summary = "Get item from order",
description = "Retrieves a specific item from an order identified by its id")
public OrderItemResponseDto getOrderItem(
@PathVariable Long orderId,
@PathVariable Long itemId,
@AuthenticationPrincipal User user) {
return orderService.getOrderItem(orderId, itemId, user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.Set;
import lombok.Data;
import org.hibernate.validator.constraints.ISBN;

Expand All @@ -16,6 +18,8 @@ public class CreateBookRequestDto {
@NotNull
@ISBN(type = ISBN.Type.ANY)
private String isbn;
@NotEmpty
private Set<Long> categoryIds;
@NotNull
@Min(0)
private BigDecimal price;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;

public record CategoryDto(
Long id,
@NotBlank
@Size(min = 4, max = 24, message = "Category name must be 4 to 24 characters long")
String name,
String description
) {
@Data
public class CategoryDto {
private Long id;
@NotBlank
@Size(min = 4, max = 24, message = "length should be 4 to 24 characters long")
private String name;
private String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.bookstore.dto.order;

import lombok.Data;

@Data
public class OrderItemResponseDto {
private Long id;
private Long bookId;
private int quantity;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package mate.academy.bookstore.dto.order;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class OrderRequestDto {
@NotBlank(message = "Shipping address can not be empty")
private String shippingAddress;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mate.academy.bookstore.dto.order;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Set;
import lombok.Data;

@Data
public class OrderResponseDto {
private Long id;
private Long userId;
private Set<OrderItemResponseDto> orderItems;
private LocalDateTime orderDate;
private BigDecimal total;
private String status;
}
11 changes: 11 additions & 0 deletions src/main/java/mate/academy/bookstore/dto/order/OrderStatusDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.bookstore.dto.order;

import jakarta.validation.constraints.NotNull;
import lombok.Data;
import mate.academy.bookstore.model.order.Status;

@Data
public class OrderStatusDto {
@NotNull
private Status status;
}
25 changes: 22 additions & 3 deletions src/main/java/mate/academy/bookstore/mapper/BookMapper.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mate.academy.bookstore.mapper;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import mate.academy.bookstore.config.MapperConfig;
import mate.academy.bookstore.dto.book.BookDto;
Expand All @@ -20,6 +21,7 @@ public interface BookMapper {

@Mapping(target = "id", ignore = true)
@Mapping(target = "deleted", ignore = true)
@Mapping(target = "categories", ignore = true)
Book toEntity(CreateBookRequestDto requestDto);

BookDtoWithoutCategoryIds toDtoWithoutCategories(Book book);
Expand All @@ -28,9 +30,26 @@ public interface BookMapper {
default void setCategoryIds(@MappingTarget BookDto bookDto, Book book) {
if (book.getCategories() == null) {
bookDto.setCategories(Collections.emptySet());
} else {
bookDto.setCategories(book.getCategories().stream()
.map(Category::getId)
.collect(Collectors.toSet()));
}
}

@AfterMapping
default void setCategories(@MappingTarget Book book, CreateBookRequestDto requestDto) {
if (requestDto.getCategoryIds() == null) {
book.setCategories(Collections.emptySet());
} else {
Set<Category> categories = requestDto.getCategoryIds().stream()
.map(id -> {
Category category = new Category();
category.setId(id);
return category;
})
.collect(Collectors.toSet());
book.setCategories(categories);
}
bookDto.setCategories(book.getCategories().stream()
.map(Category::getId)
.collect(Collectors.toSet()));
}
}
13 changes: 13 additions & 0 deletions src/main/java/mate/academy/bookstore/mapper/OrderItemMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mate.academy.bookstore.mapper;

import mate.academy.bookstore.config.MapperConfig;
import mate.academy.bookstore.dto.order.OrderItemResponseDto;
import mate.academy.bookstore.model.order.OrderItem;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(config = MapperConfig.class)
public interface OrderItemMapper {
@Mapping(target = "bookId", source = "book.id")
OrderItemResponseDto toDto(OrderItem orderItem);
}
13 changes: 13 additions & 0 deletions src/main/java/mate/academy/bookstore/mapper/OrderMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package mate.academy.bookstore.mapper;

import mate.academy.bookstore.config.MapperConfig;
import mate.academy.bookstore.dto.order.OrderResponseDto;
import mate.academy.bookstore.model.order.Order;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(config = MapperConfig.class, uses = OrderItemMapper.class)
public interface OrderMapper {
@Mapping(source = "user.id", target = "userId")
OrderResponseDto toDto(Order order);
}
4 changes: 3 additions & 1 deletion src/main/java/mate/academy/bookstore/model/Book.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;

@Getter
@Setter
@EqualsAndHashCode(of = {"id", "title", "author", "isbn", "categories"})
@EqualsAndHashCode(exclude = {"categories"})
@ToString(exclude = {"categories"})
@Entity
@SQLDelete(sql = "UPDATE books SET is_deleted = true WHERE id=?")
@SQLRestriction(value = "is_deleted=false")
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/mate/academy/bookstore/model/CartItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@EqualsAndHashCode(of = {"id", "shoppingCart", "book"})
@EqualsAndHashCode(exclude = {"shoppingCart", "book"})
@ToString(exclude = {"shoppingCart", "book"})
@Entity
@Table(name = "cart_items")
public class CartItem {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/mate/academy/bookstore/model/Category.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.SoftDelete;

@Getter
@Setter
@EqualsAndHashCode
@ToString
@Entity
@SoftDelete
@Table(name = "categories")
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/mate/academy/bookstore/model/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;

@Getter
@Setter
@EqualsAndHashCode
@ToString
@Entity
@Table(name = "roles")
public class Role implements GrantedAuthority {
Expand Down
Loading
Loading