diff --git a/order-service/README.md b/order-service/README.md
index b21155b6..919a8a35 100644
--- a/order-service/README.md
+++ b/order-service/README.md
@@ -26,5 +26,29 @@ You can also run the application using Maven as follows:
* 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.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
+```
+2.In IntelIj Open Modify Run Configuration from Main class:
+ `com.example.orderservice.OrderServiceApplication`
+Set the Environment variable value to
+```text
+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=h2
+```
+
+
### Notes
* KafkaStream DeadLetter is configured in `KafkaStreamsConfig.java`
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 5e365ac6..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
@@ -12,6 +12,8 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli.
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;
@@ -43,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
@@ -65,4 +68,11 @@ 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);
+
+ @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/repositories/OrderRepository.java b/order-service/src/main/java/com/example/orderservice/repositories/OrderRepository.java
index 8a5f38f6..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
@@ -27,6 +27,14 @@ 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);
+
+ @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 70d2c4c7..66f42989 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
@@ -13,6 +13,7 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli.
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;
@@ -128,4 +129,34 @@ 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) {
+ // 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 =
+ ordersWithOrderItems.stream()
+ .map(
+ order ->
+ CompletableFuture.supplyAsync(
+ () -> this.orderMapper.toResponse(order)))
+ .toList();
+ // Joining all completeable future to get OrderResponses
+ List orderResponse =
+ completableFutureList.stream().map(CompletableFuture::join).toList();
+ return new PagedResult<>(
+ orderResponse,
+ page.getTotalElements(),
+ page.getNumber() + 1,
+ page.getTotalPages(),
+ page.isFirst(),
+ page.isLast(),
+ page.hasNext(),
+ page.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..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
@@ -8,6 +8,7 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli.
import com.example.common.dtos.OrderDto;
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;
@@ -18,6 +19,7 @@ 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.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -131,4 +133,10 @@ public List all(
int pageSize) {
return orderKafkaStreamService.getAllOrders(pageNo, pageSize);
}
+
+ @GetMapping("/customer/{id}")
+ public ResponseEntity> ordersByCustomerId(
+ @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 37938618..7e35d74b 100644
--- a/order-service/src/main/resources/application-local.properties
+++ b/order-service/src/main/resources/application-local.properties
@@ -3,6 +3,27 @@ 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
+
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 60725110..eb8c0c74 100644
--- a/order-service/src/main/resources/application.yml
+++ b/order-service/src/main/resources/application.yml
@@ -3,7 +3,6 @@ server:
servlet:
contextPath: /${spring.application.name}
forward-headers-strategy: framework
-
spring:
application:
name: order-service
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..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
@@ -14,8 +14,7 @@ Licensed under MIT License Copyright (c) 2023 Raja Kolli.
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 +34,8 @@ class OrderRepositoryTest {
@Autowired private OrderRepository orderRepository;
@Autowired private OrderItemRepository orderItemRepository;
- @AfterEach
- void tearDown() {
+ @BeforeEach
+ void setUp() {
this.orderItemRepository.deleteAllInBatch();
this.orderRepository.deleteAllInBatch();
}
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..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
@@ -30,6 +30,8 @@ Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli.
import org.junit.jupiter.api.BeforeEach;
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 +39,7 @@ class OrderControllerIT extends AbstractIntegrationTest {
@Autowired private OrderRepository orderRepository;
private List orderList = null;
+ private OrderItem orderItem;
@BeforeEach
void setUp() {
@@ -178,7 +181,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 +194,30 @@ void shouldDeleteOrder() throws Exception {
.perform(delete("/api/orders/{id}", order.getId()))
.andExpect(status().isAccepted());
}
+
+ @Test
+ 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));
+ }
}