From 14c2fa74aa496d9cb93890c58b931da9a4843d84 Mon Sep 17 00:00:00 2001 From: manepullavamsi Date: Mon, 2 Oct 2023 13:54:49 +0530 Subject: [PATCH 1/6] Exposing an API to get orders by customer id by using pagination and updated the Readme with Tips to bring the Service up. --- order-service/README.md | 45 +++++++++++++++++++ .../repositories/OrderRepository.java | 5 +++ .../orderservice/services/OrderService.java | 22 +++++++-- .../web/controllers/OrderController.java | 13 ++++++ 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/order-service/README.md b/order-service/README.md index 4f1b3285..47103557 100644 --- a/order-service/README.md +++ b/order-service/README.md @@ -14,5 +14,50 @@ * Actuator Endpoint: http://localhost:18282/order-service/actuator * Catalog Service : http://localhost:18080/catalog-service/swagger-ui.html +## Running only this Service Locally - Tips + +To run only the Order Service locally with clean logs, you can follow these steps: + +1. Open the `pom.xml` file in your project. + +2. Locate the following dependencies and comment them: + +```xml + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +``` + +3.Spin up the Kafka and Postgres server by using below command: +```shell +docker compose up kafka postgres +``` +4.In IntelIj Open Modify Run Configuration from Main class: + com.example.orderservice.OrderServiceApplication +Set the Environment variable value to SPRING_PROFILES_ACTIVE=local + +5.In case local profile doesn't due to any issues possible not able to connect to postgresDB +Modify the Environment Variable Value to SPRING_PROFILES_ACTIVE=local1 this brings application up by connecting to H2 database. + +6.Inorder to run only this service we need to comment the catalog service calls inside Order service class +```text + //While working in local independently with out kafka service and catlog service please comment if condition. +// if (productsExistsAndInStock(productCodes)) { + +// } else { +// throw new ProductNotFoundException(productCodes); +// } + +``` + ### Notes * KafkaStream DeadLetter is configured in `KafkaStreamsConfig.java` diff --git a/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java index 8a5f38f6..c7fe8cd6 100644 --- a/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java +++ b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java @@ -6,9 +6,12 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.repositories; +import com.example.common.dtos.OrderDto; import com.example.orderservice.entities.Order; import java.util.List; import java.util.Optional; + +import com.example.orderservice.model.response.PagedResult; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.EntityGraph; @@ -26,6 +29,8 @@ public interface OrderRepository extends JpaRepository { @Query("select o from Order o join fetch o.items oi where o.id = :id") Optional findOrderById(@Param("id") Long id); + @Query(value = "select o from Order o join fetch o.items oi where o.customerId = :customerId",countQuery = "select count(o) from Order o where o.customerId=:customerId") + Page findByCustomerId(@Param("customerId") Long customerId, Pageable pageable); @Modifying @Transactional diff --git a/order-service/src/main/java/com/example/orderservice/services/OrderService.java b/order-service/src/main/java/com/example/orderservice/services/OrderService.java index 29058573..10c258a9 100644 --- a/order-service/src/main/java/com/example/orderservice/services/OrderService.java +++ b/order-service/src/main/java/com/example/orderservice/services/OrderService.java @@ -85,16 +85,17 @@ public OrderDto saveOrder(OrderRequest orderRequest) { .map(OrderItemRequest::productCode) .map(String::toUpperCase) .toList(); - if (productsExistsAndInStock(productCodes)) { + //While working in local independently with out kafka service and catlog service please comment if condition. +// if (productsExistsAndInStock(productCodes)) { Order orderEntity = this.orderMapper.orderRequestToEntity(orderRequest); Order savedOrder = getPersistedOrder(orderEntity); OrderDto persistedOrderDto = this.orderMapper.toDto(savedOrder); // Should send persistedOrderDto as it contains OrderId used for subsequent processing kafkaOrderProducer.sendOrder(persistedOrderDto); return persistedOrderDto; - } else { - throw new ProductNotFoundException(productCodes); - } +// } else { +// throw new ProductNotFoundException(productCodes); +// } } private boolean productsExistsAndInStock(List productIds) { @@ -124,4 +125,17 @@ public Optional findById(Long id) { public Optional findOrderByIdAsDto(Long id) { return orderRepository.findOrderById(id).map(this.orderMapper::toDto); } + + public PagedResult getOrdersByCustomerId(Long customerId, Pageable pageable) { + var ordersList=orderRepository.findByCustomerId(customerId,pageable); + var ordersDto=ordersList.stream().map(orderMapper::toDto).toList(); + return new PagedResult<>(ordersDto, + ordersList.getTotalElements(), + ordersList.getNumber(), + ordersList.getTotalPages(), + ordersList.isFirst(), + ordersList.isLast(), + ordersList.hasNext(), + ordersList.hasPrevious()); + } } diff --git a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java index 3ca90831..885df054 100644 --- a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java +++ b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java @@ -7,8 +7,11 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.web.controllers; import com.example.common.dtos.OrderDto; +import com.example.orderservice.entities.Order; +import com.example.orderservice.mapper.OrderMapper; import com.example.orderservice.model.request.OrderRequest; import com.example.orderservice.model.response.PagedResult; +import com.example.orderservice.repositories.OrderRepository; import com.example.orderservice.services.OrderGeneratorService; import com.example.orderservice.services.OrderKafkaStreamService; import com.example.orderservice.services.OrderService; @@ -18,6 +21,9 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. import java.net.URI; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; @@ -39,6 +45,8 @@ public class OrderController { private final OrderService orderService; private final OrderGeneratorService orderGeneratorService; private final OrderKafkaStreamService orderKafkaStreamService; + private final OrderRepository orderRepository; + private final OrderMapper orderMapper; @GetMapping public PagedResult getAllOrders( @@ -131,4 +139,9 @@ public List all( int pageSize) { return orderKafkaStreamService.getAllOrders(pageNo, pageSize); } + @GetMapping("customer/id") + public ResponseEntity> ordersByCustomerId(@RequestParam Long customerId, Pageable pageable) + { + return ResponseEntity.ok(orderService.getOrdersByCustomerId(customerId, pageable)); + } } From 3e5c9e557aa3be1e4e6be77931d391ffce4bbb63 Mon Sep 17 00:00:00 2001 From: manepullavamsi Date: Mon, 2 Oct 2023 13:57:37 +0530 Subject: [PATCH 2/6] Removing Unnecessary Commented lines in oreder service --- .../example/orderservice/services/OrderService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/order-service/src/main/java/com/example/orderservice/services/OrderService.java b/order-service/src/main/java/com/example/orderservice/services/OrderService.java index 10c258a9..345673da 100644 --- a/order-service/src/main/java/com/example/orderservice/services/OrderService.java +++ b/order-service/src/main/java/com/example/orderservice/services/OrderService.java @@ -85,17 +85,17 @@ public OrderDto saveOrder(OrderRequest orderRequest) { .map(OrderItemRequest::productCode) .map(String::toUpperCase) .toList(); - //While working in local independently with out kafka service and catlog service please comment if condition. -// if (productsExistsAndInStock(productCodes)) { + //While working in local independently without kafka service and catalog service please comment if condition. + if (productsExistsAndInStock(productCodes)) { Order orderEntity = this.orderMapper.orderRequestToEntity(orderRequest); Order savedOrder = getPersistedOrder(orderEntity); OrderDto persistedOrderDto = this.orderMapper.toDto(savedOrder); // Should send persistedOrderDto as it contains OrderId used for subsequent processing kafkaOrderProducer.sendOrder(persistedOrderDto); return persistedOrderDto; -// } else { -// throw new ProductNotFoundException(productCodes); -// } + } else { + throw new ProductNotFoundException(productCodes); + } } private boolean productsExistsAndInStock(List productIds) { From 4622067aa936bb4fbdd40b1ca2ac68e46addf3f0 Mon Sep 17 00:00:00 2001 From: manepullavamsi Date: Tue, 3 Oct 2023 10:00:03 +0530 Subject: [PATCH 3/6] Added Created Date to the order response with respective mapper also added and updated to application yaml and application-local properties and added readme.md to start application locally. --- order-service/README.md | 46 ++++++------------- .../example/common/dtos/OrderResponse.java | 37 +++++++++++++++ .../orderservice/mapper/OrderMapper.java | 4 ++ .../repositories/OrderRepository.java | 8 ++-- .../orderservice/services/OrderService.java | 20 ++++++-- .../web/controllers/OrderController.java | 13 ++---- .../resources/application-local.properties | 2 + .../src/main/resources/application.yml | 15 +++++- 8 files changed, 93 insertions(+), 52 deletions(-) create mode 100644 order-service/src/main/java/com/example/common/dtos/OrderResponse.java diff --git a/order-service/README.md b/order-service/README.md index 47103557..36eb9cd9 100644 --- a/order-service/README.md +++ b/order-service/README.md @@ -18,46 +18,26 @@ To run only the Order Service locally with clean logs, you can follow these steps: -1. Open the `pom.xml` file in your project. - -2. Locate the following dependencies and comment them: - -```xml - - org.springframework.cloud - spring-cloud-starter-config - - - org.springframework.cloud - spring-cloud-starter-loadbalancer - - - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - -``` -3.Spin up the Kafka and Postgres server by using below command: + +1.start the Kafka and Postgres servers by using below command(You should be inside appropriate directory and docker setup should be done :- ) : ```shell docker compose up kafka postgres ``` -4.In IntelIj Open Modify Run Configuration from Main class: - com.example.orderservice.OrderServiceApplication -Set the Environment variable value to SPRING_PROFILES_ACTIVE=local - -5.In case local profile doesn't due to any issues possible not able to connect to postgresDB -Modify the Environment Variable Value to SPRING_PROFILES_ACTIVE=local1 this brings application up by connecting to H2 database. - -6.Inorder to run only this service we need to comment the catalog service calls inside Order service class +2.In IntelIj Open Modify Run Configuration from Main class: + `com.example.orderservice.OrderServiceApplication` +Set the Environment variable value to ```text - //While working in local independently with out kafka service and catlog service please comment if condition. -// if (productsExistsAndInStock(productCodes)) { - -// } else { -// throw new ProductNotFoundException(productCodes); -// } +SPRING_PROFILES_ACTIVE=local +``` +3.In case local profile doesn't due to any issues possible not able to connect to postgresDB +Modify the Environment Variable Value as below this brings application up by connecting to H2 database. +```text +SPRING_PROFILES_ACTIVE=local1 ``` + + ### Notes * KafkaStream DeadLetter is configured in `KafkaStreamsConfig.java` diff --git a/order-service/src/main/java/com/example/common/dtos/OrderResponse.java b/order-service/src/main/java/com/example/common/dtos/OrderResponse.java new file mode 100644 index 00000000..860ae06e --- /dev/null +++ b/order-service/src/main/java/com/example/common/dtos/OrderResponse.java @@ -0,0 +1,37 @@ +/*** +

+ Licensed under MIT License Copyright (c) 2023 Raja Kolli. +

+***/ + +package com.example.common.dtos; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Positive; +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class OrderResponse implements Serializable { + + private Long orderId; + + @Positive(message = "CustomerId should be positive") + private Long customerId; + + private String status = "NEW"; + + private String source; + + private LocalDateTime createdDate; + + @NotEmpty(message = "Order without items not valid") + private List items = new ArrayList<>(); +} diff --git a/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java b/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java index 5e365ac6..6c8ef4dc 100644 --- a/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java +++ b/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java @@ -8,6 +8,7 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. import com.example.common.dtos.OrderDto; import com.example.common.dtos.OrderItemDto; +import com.example.common.dtos.OrderResponse; import com.example.orderservice.entities.Order; import com.example.orderservice.entities.OrderItem; import com.example.orderservice.model.request.OrderItemRequest; @@ -65,4 +66,7 @@ default void addOrderItemRequestToOrderEntity( @Mapping(target = "lastModifiedBy", ignore = true) @Mapping(target = "lastModifiedDate", ignore = true) void updateOrderFromOrderRequest(OrderRequest orderRequest, @MappingTarget Order order); + + @Mapping(source = "id", target = "orderId") + OrderResponse toResponse(Order order); } diff --git a/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java index c7fe8cd6..4c4a8304 100644 --- a/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java +++ b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java @@ -6,12 +6,9 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.repositories; -import com.example.common.dtos.OrderDto; import com.example.orderservice.entities.Order; import java.util.List; import java.util.Optional; - -import com.example.orderservice.model.response.PagedResult; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.EntityGraph; @@ -29,7 +26,10 @@ public interface OrderRepository extends JpaRepository { @Query("select o from Order o join fetch o.items oi where o.id = :id") Optional findOrderById(@Param("id") Long id); - @Query(value = "select o from Order o join fetch o.items oi where o.customerId = :customerId",countQuery = "select count(o) from Order o where o.customerId=:customerId") + + @Query( + value = "select o from Order o join fetch o.items oi where o.customerId = :customerId", + countQuery = "select count(o) from Order o where o.customerId=:customerId") Page findByCustomerId(@Param("customerId") Long customerId, Pageable pageable); @Modifying diff --git a/order-service/src/main/java/com/example/orderservice/services/OrderService.java b/order-service/src/main/java/com/example/orderservice/services/OrderService.java index 345673da..a7acbe0e 100644 --- a/order-service/src/main/java/com/example/orderservice/services/OrderService.java +++ b/order-service/src/main/java/com/example/orderservice/services/OrderService.java @@ -7,6 +7,7 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.services; import com.example.common.dtos.OrderDto; +import com.example.common.dtos.OrderResponse; import com.example.orderservice.config.logging.Loggable; import com.example.orderservice.entities.Order; import com.example.orderservice.exception.ProductNotFoundException; @@ -85,7 +86,6 @@ public OrderDto saveOrder(OrderRequest orderRequest) { .map(OrderItemRequest::productCode) .map(String::toUpperCase) .toList(); - //While working in local independently without kafka service and catalog service please comment if condition. if (productsExistsAndInStock(productCodes)) { Order orderEntity = this.orderMapper.orderRequestToEntity(orderRequest); Order savedOrder = getPersistedOrder(orderEntity); @@ -126,10 +126,20 @@ public Optional findOrderByIdAsDto(Long id) { return orderRepository.findOrderById(id).map(this.orderMapper::toDto); } - public PagedResult getOrdersByCustomerId(Long customerId, Pageable pageable) { - var ordersList=orderRepository.findByCustomerId(customerId,pageable); - var ordersDto=ordersList.stream().map(orderMapper::toDto).toList(); - return new PagedResult<>(ordersDto, + public PagedResult getOrdersByCustomerId(Long customerId, Pageable pageable) { + var ordersList = orderRepository.findByCustomerId(customerId, pageable); + List> completableFutureList = + ordersList.stream() + .map( + order -> + CompletableFuture.supplyAsync( + () -> this.orderMapper.toResponse(order))) + .toList(); + // Joining all completeable future to get DTOs + List orderListDto = + completableFutureList.stream().map(CompletableFuture::join).toList(); + return new PagedResult<>( + orderListDto, ordersList.getTotalElements(), ordersList.getNumber(), ordersList.getTotalPages(), diff --git a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java index 885df054..7e4de654 100644 --- a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java +++ b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java @@ -7,11 +7,9 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.web.controllers; import com.example.common.dtos.OrderDto; -import com.example.orderservice.entities.Order; -import com.example.orderservice.mapper.OrderMapper; +import com.example.common.dtos.OrderResponse; import com.example.orderservice.model.request.OrderRequest; import com.example.orderservice.model.response.PagedResult; -import com.example.orderservice.repositories.OrderRepository; import com.example.orderservice.services.OrderGeneratorService; import com.example.orderservice.services.OrderKafkaStreamService; import com.example.orderservice.services.OrderService; @@ -21,8 +19,6 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. import java.net.URI; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; @@ -45,8 +41,6 @@ public class OrderController { private final OrderService orderService; private final OrderGeneratorService orderGeneratorService; private final OrderKafkaStreamService orderKafkaStreamService; - private final OrderRepository orderRepository; - private final OrderMapper orderMapper; @GetMapping public PagedResult getAllOrders( @@ -139,9 +133,10 @@ public List all( int pageSize) { return orderKafkaStreamService.getAllOrders(pageNo, pageSize); } + @GetMapping("customer/id") - public ResponseEntity> ordersByCustomerId(@RequestParam Long customerId, Pageable pageable) - { + public ResponseEntity> ordersByCustomerId( + @RequestParam Long customerId, Pageable pageable) { return ResponseEntity.ok(orderService.getOrdersByCustomerId(customerId, pageable)); } } diff --git a/order-service/src/main/resources/application-local.properties b/order-service/src/main/resources/application-local.properties index deac9bd4..f60fc032 100644 --- a/order-service/src/main/resources/application-local.properties +++ b/order-service/src/main/resources/application-local.properties @@ -3,6 +3,8 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/appdb spring.datasource.username=appuser spring.datasource.password=secret +spring.cloud.config.enabled=false + application.catalog-service-url=http://localhost:18080/catalog-service spring.config.import=optional:configserver:${CONFIG_SERVER:http://localhost:8888}/ diff --git a/order-service/src/main/resources/application.yml b/order-service/src/main/resources/application.yml index 8518bbc1..dcfc7d49 100644 --- a/order-service/src/main/resources/application.yml +++ b/order-service/src/main/resources/application.yml @@ -3,8 +3,21 @@ server: servlet: contextPath: /${spring.application.name} forward-headers-strategy: framework - +application: + catalog-service-url: http://localhost:18080/catalog-service spring: + cloud: + config: + enabled: false + datasource: + url: jdbc:h2:file:/data/demo + username: sa + password: password + driverClassName: org.h2.Driver + jpa: + spring.jpa.database-platform: org.hibernate.dialect.H2Dialect + config: + import: optional:configserver:${CONFIG_SERVER:http://localhost:8888}/ application: name: order-service threads.virtual.enabled: true From 7fc4305596a1375ef2d2639284e334190bad7e81 Mon Sep 17 00:00:00 2001 From: manepullavamsi Date: Thu, 5 Oct 2023 23:31:48 +0530 Subject: [PATCH 4/6] Addressing code review comments --- .../example/common/dtos/OrderResponse.java | 37 ------------------- .../orderservice/entities/OrderItem.java | 16 ++++---- .../orderservice/mapper/OrderMapper.java | 8 +++- .../model/response/OrderItemResponse.java | 12 ++++++ .../model/response/OrderResponse.java | 18 +++++++++ .../orderservice/services/OrderService.java | 2 +- .../web/controllers/OrderController.java | 8 ++-- .../src/main/resources/application-h2.yml | 17 +++++++++ .../resources/application-local.properties | 1 + .../src/main/resources/application.yml | 14 ------- .../repositories/OrderRepositoryTest.java | 22 +++++++++-- .../example/orderservice/util/TestData.java | 2 +- .../web/controllers/OrderControllerIT.java | 33 ++++++++++++++++- 13 files changed, 118 insertions(+), 72 deletions(-) delete mode 100644 order-service/src/main/java/com/example/common/dtos/OrderResponse.java create mode 100644 order-service/src/main/java/com/example/orderservice/model/response/OrderItemResponse.java create mode 100644 order-service/src/main/java/com/example/orderservice/model/response/OrderResponse.java create mode 100644 order-service/src/main/resources/application-h2.yml diff --git a/order-service/src/main/java/com/example/common/dtos/OrderResponse.java b/order-service/src/main/java/com/example/common/dtos/OrderResponse.java deleted file mode 100644 index 860ae06e..00000000 --- a/order-service/src/main/java/com/example/common/dtos/OrderResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -/*** -

- Licensed under MIT License Copyright (c) 2023 Raja Kolli. -

-***/ - -package com.example.common.dtos; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.Positive; -import java.io.Serializable; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class OrderResponse implements Serializable { - - private Long orderId; - - @Positive(message = "CustomerId should be positive") - private Long customerId; - - private String status = "NEW"; - - private String source; - - private LocalDateTime createdDate; - - @NotEmpty(message = "Order without items not valid") - private List items = new ArrayList<>(); -} diff --git a/order-service/src/main/java/com/example/orderservice/entities/OrderItem.java b/order-service/src/main/java/com/example/orderservice/entities/OrderItem.java index 4be5736e..61994331 100644 --- a/order-service/src/main/java/com/example/orderservice/entities/OrderItem.java +++ b/order-service/src/main/java/com/example/orderservice/entities/OrderItem.java @@ -6,15 +6,7 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.entities; -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.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.*; import java.math.BigDecimal; import java.util.Objects; import lombok.AllArgsConstructor; @@ -51,6 +43,12 @@ public class OrderItem { @Column(columnDefinition = "NUMERIC(19,2)") private BigDecimal productPrice; + @Transient private BigDecimal price; + + public BigDecimal getPrice() { + return productPrice.multiply(new BigDecimal(quantity)); + } + @ManyToOne(fetch = FetchType.LAZY) @ToString.Exclude private Order order; diff --git a/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java b/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java index 6c8ef4dc..24912daa 100644 --- a/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java +++ b/order-service/src/main/java/com/example/orderservice/mapper/OrderMapper.java @@ -8,11 +8,12 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. import com.example.common.dtos.OrderDto; import com.example.common.dtos.OrderItemDto; -import com.example.common.dtos.OrderResponse; import com.example.orderservice.entities.Order; import com.example.orderservice.entities.OrderItem; import com.example.orderservice.model.request.OrderItemRequest; import com.example.orderservice.model.request.OrderRequest; +import com.example.orderservice.model.response.OrderItemResponse; +import com.example.orderservice.model.response.OrderResponse; import org.mapstruct.AfterMapping; import org.mapstruct.DecoratedWith; import org.mapstruct.Mapper; @@ -44,6 +45,7 @@ public interface OrderMapper { @Mapping(target = "id", ignore = true) @Mapping(target = "order", ignore = true) + @Mapping(target = "price", ignore = true) OrderItem orderItemRequestToOrderItem(OrderItemRequest orderItemRequest); @AfterMapping @@ -69,4 +71,8 @@ default void addOrderItemRequestToOrderEntity( @Mapping(source = "id", target = "orderId") OrderResponse toResponse(Order order); + + @Mapping(target = "itemId", source = "id") + @Mapping(target = "productId", source = "productCode") + OrderItemResponse orderItemToOrderItemResponse(OrderItem orderItem); } diff --git a/order-service/src/main/java/com/example/orderservice/model/response/OrderItemResponse.java b/order-service/src/main/java/com/example/orderservice/model/response/OrderItemResponse.java new file mode 100644 index 00000000..e90f5553 --- /dev/null +++ b/order-service/src/main/java/com/example/orderservice/model/response/OrderItemResponse.java @@ -0,0 +1,12 @@ +/*** +

+ Licensed under MIT License Copyright (c) 2023 Raja Kolli. +

+***/ + +package com.example.orderservice.model.response; + +import java.math.BigDecimal; + +public record OrderItemResponse( + Long itemId, String productId, int quantity, BigDecimal productPrice, BigDecimal price) {} diff --git a/order-service/src/main/java/com/example/orderservice/model/response/OrderResponse.java b/order-service/src/main/java/com/example/orderservice/model/response/OrderResponse.java new file mode 100644 index 00000000..fb2fc30c --- /dev/null +++ b/order-service/src/main/java/com/example/orderservice/model/response/OrderResponse.java @@ -0,0 +1,18 @@ +/*** +

+ Licensed under MIT License Copyright (c) 2023 Raja Kolli. +

+***/ + +package com.example.orderservice.model.response; + +import java.time.LocalDateTime; +import java.util.List; + +public record OrderResponse( + Long orderId, + Long customerId, + String status, + String source, + LocalDateTime createdDate, + List items) {} diff --git a/order-service/src/main/java/com/example/orderservice/services/OrderService.java b/order-service/src/main/java/com/example/orderservice/services/OrderService.java index a7acbe0e..dd0637da 100644 --- a/order-service/src/main/java/com/example/orderservice/services/OrderService.java +++ b/order-service/src/main/java/com/example/orderservice/services/OrderService.java @@ -7,13 +7,13 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.services; import com.example.common.dtos.OrderDto; -import com.example.common.dtos.OrderResponse; import com.example.orderservice.config.logging.Loggable; import com.example.orderservice.entities.Order; import com.example.orderservice.exception.ProductNotFoundException; import com.example.orderservice.mapper.OrderMapper; import com.example.orderservice.model.request.OrderItemRequest; import com.example.orderservice.model.request.OrderRequest; +import com.example.orderservice.model.response.OrderResponse; import com.example.orderservice.model.response.PagedResult; import com.example.orderservice.repositories.OrderRepository; import java.util.List; diff --git a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java index 7e4de654..2f6a4370 100644 --- a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java +++ b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java @@ -7,8 +7,8 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. package com.example.orderservice.web.controllers; import com.example.common.dtos.OrderDto; -import com.example.common.dtos.OrderResponse; import com.example.orderservice.model.request.OrderRequest; +import com.example.orderservice.model.response.OrderResponse; import com.example.orderservice.model.response.PagedResult; import com.example.orderservice.services.OrderGeneratorService; import com.example.orderservice.services.OrderKafkaStreamService; @@ -134,9 +134,9 @@ public List all( return orderKafkaStreamService.getAllOrders(pageNo, pageSize); } - @GetMapping("customer/id") + @GetMapping("customer/{id}") public ResponseEntity> ordersByCustomerId( - @RequestParam Long customerId, Pageable pageable) { - return ResponseEntity.ok(orderService.getOrdersByCustomerId(customerId, pageable)); + @PathVariable Long id, Pageable pageable) { + return ResponseEntity.ok(orderService.getOrdersByCustomerId(id, pageable)); } } diff --git a/order-service/src/main/resources/application-h2.yml b/order-service/src/main/resources/application-h2.yml new file mode 100644 index 00000000..d41d5b4c --- /dev/null +++ b/order-service/src/main/resources/application-h2.yml @@ -0,0 +1,17 @@ +spring: + cloud: + config: + enabled: false + discovery: + enabled: false + datasource: + url: jdbc:h2:file:/data/demo + username: sa + password: password + driverClassName: org.h2.Driver + jpa: + spring.jpa.database-platform: org.hibernate.dialect.H2Dialect + config: + import: optional:configserver:${CONFIG_SERVER:http://localhost:8888}/ +application: + catalog-service-url: http://localhost:18080/catalog-service diff --git a/order-service/src/main/resources/application-local.properties b/order-service/src/main/resources/application-local.properties index f60fc032..5b70e1cc 100644 --- a/order-service/src/main/resources/application-local.properties +++ b/order-service/src/main/resources/application-local.properties @@ -4,6 +4,7 @@ spring.datasource.username=appuser spring.datasource.password=secret spring.cloud.config.enabled=false +spring.cloud.discovery.enabled=false application.catalog-service-url=http://localhost:18080/catalog-service diff --git a/order-service/src/main/resources/application.yml b/order-service/src/main/resources/application.yml index dcfc7d49..7c1cccc6 100644 --- a/order-service/src/main/resources/application.yml +++ b/order-service/src/main/resources/application.yml @@ -3,21 +3,7 @@ server: servlet: contextPath: /${spring.application.name} forward-headers-strategy: framework -application: - catalog-service-url: http://localhost:18080/catalog-service spring: - cloud: - config: - enabled: false - datasource: - url: jdbc:h2:file:/data/demo - username: sa - password: password - driverClassName: org.h2.Driver - jpa: - spring.jpa.database-platform: org.hibernate.dialect.H2Dialect - config: - import: optional:configserver:${CONFIG_SERVER:http://localhost:8888}/ application: name: order-service threads.virtual.enabled: true diff --git a/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java b/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java index 337c077c..11fdee22 100644 --- a/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java +++ b/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java @@ -8,14 +8,14 @@ Licensed under MIT License Copyright (c) 2023 Raja Kolli. import static com.example.orderservice.utils.AppConstants.PROFILE_TEST; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import com.example.orderservice.common.PostGreSQLContainer; import com.example.orderservice.entities.Order; import com.example.orderservice.util.TestData; import java.util.ArrayList; import java.util.List; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -35,8 +35,8 @@ class OrderRepositoryTest { @Autowired private OrderRepository orderRepository; @Autowired private OrderItemRepository orderItemRepository; - @AfterEach - void tearDown() { + @BeforeEach + void setUp() { this.orderItemRepository.deleteAllInBatch(); this.orderRepository.deleteAllInBatch(); } @@ -66,4 +66,18 @@ void findAllOrders() { assertThat(page.getNumberOfElements()).isEqualTo(5); assertThat(page.getContent()).isNotEmpty().hasSize(5); } + + @Test + @DisplayName("getOrdersByCustomerIdTest") + void getOrdersByCustomerIdTest() { + List orderList = new ArrayList<>(); + for (int i = 0; i < 15; i++) { + orderList.add(TestData.getOrder()); + } + this.orderRepository.saveAll(orderList); + Pageable pageable = PageRequest.of(0, 1); + Page ordersPage = orderRepository.findByCustomerId(1L, pageable); + assertEquals(15, ordersPage.getTotalPages()); + assertEquals(15, ordersPage.getTotalElements()); + } } diff --git a/order-service/src/test/java/com/example/orderservice/util/TestData.java b/order-service/src/test/java/com/example/orderservice/util/TestData.java index fc132441..d55bf593 100644 --- a/order-service/src/test/java/com/example/orderservice/util/TestData.java +++ b/order-service/src/test/java/com/example/orderservice/util/TestData.java @@ -22,7 +22,7 @@ public static Order getOrder() { OrderItem orderItem = new OrderItem(); orderItem.setProductCode("Product1"); orderItem.setQuantity(10); - orderItem.setProductPrice(BigDecimal.TEN); + orderItem.setProductPrice(new BigDecimal("10.1")); OrderItem orderItem1 = new OrderItem(); orderItem1.setProductCode("Product2"); orderItem1.setQuantity(100); diff --git a/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java b/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java index 579618d1..86790310 100644 --- a/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java +++ b/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java @@ -28,8 +28,11 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; class OrderControllerIT extends AbstractIntegrationTest { @@ -37,6 +40,7 @@ class OrderControllerIT extends AbstractIntegrationTest { @Autowired private OrderRepository orderRepository; private List orderList = null; + private OrderItem orderItem; @BeforeEach void setUp() { @@ -178,7 +182,7 @@ void shouldUpdateOrder() throws Exception { .andExpect(jsonPath("$.status", is("NEW"))) .andExpect(jsonPath("$.items.size()", is(2))) .andExpect(jsonPath("$.items[0].quantity", is(110))) - .andExpect(jsonPath("$.items[0].price", is(1100))) + .andExpect(jsonPath("$.items[0].price", is(1111.0))) .andExpect(jsonPath("$.items[1].quantity", is(100))) .andExpect(jsonPath("$.items[1].price", is(100))); } @@ -191,4 +195,31 @@ void shouldDeleteOrder() throws Exception { .perform(delete("/api/orders/{id}", order.getId())) .andExpect(status().isAccepted()); } + + @Test + @Disabled + void shouldFindOrdersByCustomersId() throws Exception { + OrderItem orderItem = orderList.get(0).getItems().get(0); + + Pageable pageable = PageRequest.of(0, 1); + mockMvc.perform( + get("/api/orders/customer/{id}", orderList.get(0).getCustomerId()) + .queryParam("page", "0") + .queryParam("size", "1")) + .andExpect(status().isOk()) + .andExpect( + jsonPath( + "$.data[0].customerId", + is(orderList.get(0).getCustomerId()), + Long.class)) + .andExpect(jsonPath("$.totalElements", is(orderList.size()))) + .andExpect( + jsonPath( + "$.data[0].items[0].price", + is( + orderItem + .getProductPrice() + .multiply(new BigDecimal(orderItem.getQuantity()))), + BigDecimal.class)); + } } From e0fc3965406197ef81503941f8a402c95c7aff4f Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Oct 2023 18:33:40 +0000 Subject: [PATCH 5/6] fixes junits --- order-service/README.md | 3 +- .../repositories/OrderRepository.java | 3 ++ .../orderservice/services/OrderService.java | 31 ++++++++++++------- .../resources/application-local.properties | 18 +++++++++++ .../repositories/OrderRepositoryTest.java | 15 --------- .../web/controllers/OrderControllerIT.java | 2 -- 6 files changed, 41 insertions(+), 31 deletions(-) diff --git a/order-service/README.md b/order-service/README.md index 36eb9cd9..2f008621 100644 --- a/order-service/README.md +++ b/order-service/README.md @@ -34,9 +34,8 @@ SPRING_PROFILES_ACTIVE=local 3.In case local profile doesn't due to any issues possible not able to connect to postgresDB Modify the Environment Variable Value as below this brings application up by connecting to H2 database. ```text -SPRING_PROFILES_ACTIVE=local1 +SPRING_PROFILES_ACTIVE=h2 ``` - ### Notes diff --git a/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java index 4c4a8304..170f65b6 100644 --- a/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java +++ b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java @@ -32,6 +32,9 @@ public interface OrderRepository extends JpaRepository { countQuery = "select count(o) from Order o where o.customerId=:customerId") Page findByCustomerId(@Param("customerId") Long customerId, Pageable pageable); + @Query("select o.id from Order o where o.customerId = :customerId") + Page findAllOrdersByCustomerId(@Param("customerId") Long customerId, Pageable pageable); + @Modifying @Transactional @Query("update Order o set o.status =:status, o.source =:source where o.id = :id") diff --git a/order-service/src/main/java/com/example/orderservice/services/OrderService.java b/order-service/src/main/java/com/example/orderservice/services/OrderService.java index dd0637da..95d6737d 100644 --- a/order-service/src/main/java/com/example/orderservice/services/OrderService.java +++ b/order-service/src/main/java/com/example/orderservice/services/OrderService.java @@ -127,25 +127,32 @@ public Optional findOrderByIdAsDto(Long id) { } public PagedResult getOrdersByCustomerId(Long customerId, Pageable pageable) { - var ordersList = orderRepository.findByCustomerId(customerId, pageable); + // Error:: JpaSystem firstResult/maxResults specified with collection fetch. In memory + // pagination was about to be applied. Failing because 'Fail on pagination over collection + // fetch' is enabled. + // To fix above error Fetches only ParentEntities ids and then using keys fetch Data. + Page page = orderRepository.findAllOrdersByCustomerId(customerId, pageable); + // fetching parentAlongWithChildEntries + List ordersWithOrderItems = orderRepository.findByIdIn(page.getContent()); + // Mapping Order to OrderResponse CompletableFuture List> completableFutureList = - ordersList.stream() + ordersWithOrderItems.stream() .map( order -> CompletableFuture.supplyAsync( () -> this.orderMapper.toResponse(order))) .toList(); - // Joining all completeable future to get DTOs - List orderListDto = + // Joining all completeable future to get OrderResponses + List orderResponse = completableFutureList.stream().map(CompletableFuture::join).toList(); return new PagedResult<>( - orderListDto, - ordersList.getTotalElements(), - ordersList.getNumber(), - ordersList.getTotalPages(), - ordersList.isFirst(), - ordersList.isLast(), - ordersList.hasNext(), - ordersList.hasPrevious()); + orderResponse, + page.getTotalElements(), + page.getNumber() + 1, + page.getTotalPages(), + page.isFirst(), + page.isLast(), + page.hasNext(), + page.hasPrevious()); } } diff --git a/order-service/src/main/resources/application-local.properties b/order-service/src/main/resources/application-local.properties index 5b70e1cc..6ef60c72 100644 --- a/order-service/src/main/resources/application-local.properties +++ b/order-service/src/main/resources/application-local.properties @@ -3,6 +3,24 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/appdb spring.datasource.username=appuser spring.datasource.password=secret +################ Database ##################### +spring.jpa.show-sql=true +spring.jpa.open-in-view=false +spring.datasource.hikari.auto-commit=false +spring.jpa.hibernate.ddl-auto=none +#spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.jdbc.time_zone=UTC +spring.jpa.properties.hibernate.generate_statistics=false +spring.jpa.properties.hibernate.jdbc.batch_size=25 +spring.jpa.properties.hibernate.order_inserts=true +spring.jpa.properties.hibernate.order_updates=true +spring.jpa.properties.hibernate.query.fail_on_pagination_over_collection_fetch=true +spring.jpa.properties.hibernate.query.in_clause_parameter_padding=true +spring.jpa.properties.hibernate.connection.provider_disables_autocommit=true +spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true + +spring.mvc.problemdetails.enabled=true + spring.cloud.config.enabled=false spring.cloud.discovery.enabled=false diff --git a/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java b/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java index 11fdee22..3aa42708 100644 --- a/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java +++ b/order-service/src/test/java/com/example/orderservice/repositories/OrderRepositoryTest.java @@ -8,7 +8,6 @@ Licensed under MIT License Copyright (c) 2023 Raja Kolli. import static com.example.orderservice.utils.AppConstants.PROFILE_TEST; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; import com.example.orderservice.common.PostGreSQLContainer; import com.example.orderservice.entities.Order; @@ -66,18 +65,4 @@ void findAllOrders() { assertThat(page.getNumberOfElements()).isEqualTo(5); assertThat(page.getContent()).isNotEmpty().hasSize(5); } - - @Test - @DisplayName("getOrdersByCustomerIdTest") - void getOrdersByCustomerIdTest() { - List orderList = new ArrayList<>(); - for (int i = 0; i < 15; i++) { - orderList.add(TestData.getOrder()); - } - this.orderRepository.saveAll(orderList); - Pageable pageable = PageRequest.of(0, 1); - Page ordersPage = orderRepository.findByCustomerId(1L, pageable); - assertEquals(15, ordersPage.getTotalPages()); - assertEquals(15, ordersPage.getTotalElements()); - } } diff --git a/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java b/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java index 86790310..4f09cf6a 100644 --- a/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java +++ b/order-service/src/test/java/com/example/orderservice/web/controllers/OrderControllerIT.java @@ -28,7 +28,6 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; @@ -197,7 +196,6 @@ void shouldDeleteOrder() throws Exception { } @Test - @Disabled void shouldFindOrdersByCustomersId() throws Exception { OrderItem orderItem = orderList.get(0).getItems().get(0); From 1f19cd3bca26ebfd495e326b0bacf7aec36d0ce2 Mon Sep 17 00:00:00 2001 From: Raja Kolli Date: Thu, 5 Oct 2023 18:46:06 +0000 Subject: [PATCH 6/6] feat: add path --- .../example/orderservice/web/controllers/OrderController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java index 2f6a4370..ad30f6ab 100644 --- a/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java +++ b/order-service/src/main/java/com/example/orderservice/web/controllers/OrderController.java @@ -134,7 +134,7 @@ public List all( return orderKafkaStreamService.getAllOrders(pageNo, pageSize); } - @GetMapping("customer/{id}") + @GetMapping("/customer/{id}") public ResponseEntity> ordersByCustomerId( @PathVariable Long id, Pageable pageable) { return ResponseEntity.ok(orderService.getOrdersByCustomerId(id, pageable));