Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3c7318e
added project base
MohammmedAhmed8 May 2, 2025
b0734f8
added .github file
MohammmedAhmed8 May 2, 2025
280a921
fixed linter issue
MohammmedAhmed8 May 2, 2025
c059be8
fixed linter issue
MohammmedAhmed8 May 2, 2025
d27f2e1
added order model, repository, service and controller
MohammmedAhmed8 May 3, 2025
05f9c90
fixed linter issues
MohammmedAhmed8 May 3, 2025
c26372d
Add endpoints to get orders by user ID, cancel orders, and update ord…
oaly2 May 4, 2025
da784fc
Fix linter issue
oaly2 May 4, 2025
5d20343
Resolve Comments
oaly2 May 4, 2025
8cc4b16
Adjusted lint checks
oaly2 May 4, 2025
7191cb3
added swagger and logger in the controller
MohammmedAhmed8 May 6, 2025
3542af2
Merge branch 'dev' into Crud-Operations
MohammmedAhmed8 May 6, 2025
fd5c1e5
fixed linter issues
MohammmedAhmed8 May 6, 2025
a5423a9
Added Global Exception Handler and Endpoint to Check out an order
oaly2 May 8, 2025
71d8612
Create docker-compose.yml
oaly2 May 10, 2025
ae077b0
Add DockerFile
oaly2 May 11, 2025
5e4bb96
Removed Checkout endpoint and Adjusted OrderStatus enum
oaly2 May 12, 2025
94a14a8
added order address
MohammmedAhmed8 May 12, 2025
afcea40
fixed linter issues
MohammmedAhmed8 May 12, 2025
7a36737
Merge branch 'dev' into Omar/3-endpoints
oaly2 May 12, 2025
306e5a9
Merge branch 'Crud-Operations' into Omar/3-endpoints
oaly2 May 12, 2025
d8dd9ff
Merge pull request #3 from Podzilla/Omar/3-endpoints
oaly2 May 12, 2025
e881d83
fixed errors
MohammmedAhmed8 May 12, 2025
c911c79
Chnage long -> UUID in OrderService
oaly2 May 12, 2025
3bf3427
Merge branch 'Crud-Operations' of https://github.com/Podzilla/order i…
oaly2 May 12, 2025
667adea
long -> UUID in OrderController
oaly2 May 12, 2025
5950a26
Add stock reservation and order placed messaging via RabbitMQ
oaly2 May 13, 2025
7d6888d
modified order consumer and order producer
MohammmedAhmed8 May 17, 2025
61eb999
removed unused classes
MohammmedAhmed8 May 17, 2025
b7c78d0
fixed linter issues
MohammmedAhmed8 May 17, 2025
d5ac88e
added track order
MohammmedAhmed8 May 17, 2025
db27a0b
modified get location url
MohammmedAhmed8 May 17, 2025
de6f836
Merge branch 'dev' into Crud-Operations
MohammmedAhmed8 May 17, 2025
784d7d4
fixed linter issue
MohammmedAhmed8 May 17, 2025
76687d4
added confirmation type, fixed linter issue
MohammmedAhmed8 May 17, 2025
4ffb195
added order long - lat
MohammmedAhmed8 May 17, 2025
bbeff45
fixed linter issue
MohammmedAhmed8 May 17, 2025
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
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ services:
ports:
- "5432:5432"

rabbitmq:
image: rabbitmq:3-management
container_name: rabbitmq
ports:
- "15672:15672"
- "5672:5672"
environment:
RABBITMQ_DEFAULT_USER: user
RABBITMQ_DEFAULT_PASS: password

loki:
image: grafana/loki:3.5.0
container_name: loki
Expand Down
17 changes: 16 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,22 @@
<properties>
<java.version>23</java.version>
</properties>
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
Expand Down Expand Up @@ -82,8 +93,12 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.Podzilla</groupId>
<artifactId>podzilla-utils-lib</artifactId>
<version>v1.1.11</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/podzilla/order/OrderApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = {"com.podzilla"})
public class OrderApplication {

public static void main(final String[] args) {
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/podzilla/order/config/WebClientConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.podzilla.order.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;

@Configuration
public class WebClientConfig {

@Bean
public WebClient webClient() {
return WebClient.builder().build();
}
}
42 changes: 39 additions & 3 deletions src/main/java/com/podzilla/order/controller/OrderController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.podzilla.order.controller;

import com.podzilla.order.model.Order;
import com.podzilla.order.model.OrderLocation;
import com.podzilla.order.model.OrderStatus;
import com.podzilla.order.service.OrderService;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -13,6 +14,7 @@
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.PatchMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand All @@ -38,6 +40,8 @@ public OrderController(final OrderService orderService) {
this.orderService = orderService;
}



@PostMapping
@Operation(summary = "Create a new order",
description = "Creates a new order and returns the created order")
Expand All @@ -49,6 +53,8 @@ public ResponseEntity<Order> createOrder(@RequestBody final Order order) {
return ResponseEntity.ok(createdOrder);
}



@GetMapping
@Operation(summary = "Get all orders",
description = "Returns a list of all orders")
Expand All @@ -59,6 +65,8 @@ public ResponseEntity<List<Order>> getAllOrders() {
return ResponseEntity.ok(orders);
}



@GetMapping("/{id}")
@Operation(summary = "Get order by ID",
description = "Returns an order by its ID")
Expand All @@ -70,7 +78,9 @@ public ResponseEntity<Order> getOrderById(@PathVariable final UUID id) {
return ResponseEntity.ok(order);
}

@PutMapping("/{id}")


@PatchMapping("/{id}")
@Operation(summary = "Update an order",
description = "Updates an existing order and returns the updated "
+ "order")
Expand All @@ -83,6 +93,8 @@ public ResponseEntity<Order> updateOrder(@PathVariable final UUID id,
return ResponseEntity.ok(updatedOrder);
}



@DeleteMapping("/{id}")
@Operation(summary = "Delete an order",
description = "Deletes an order by its ID")
Expand All @@ -94,6 +106,8 @@ public ResponseEntity<Void> deleteOrder(@PathVariable final UUID id) {
return ResponseEntity.noContent().build();
}



@GetMapping("/user/{userId}")
@Operation(
summary = "Get order by user ID",
Expand All @@ -109,6 +123,8 @@ public ResponseEntity<Optional<Order>> getOrderByUserId(
return ResponseEntity.ok(order);
}



@PutMapping("/cancel/{id}")
@Operation(
summary = "Cancel order",
Expand All @@ -117,12 +133,15 @@ public ResponseEntity<Optional<Order>> getOrderByUserId(
@ApiResponse(
responseCode = "200", description = "Order cancelled"
)
public ResponseEntity<Order> cancelOrder(@PathVariable final UUID id) {
Order order = orderService.cancelOrder(id);
public ResponseEntity<Order> cancelOrder(@PathVariable final UUID id,
@RequestBody final String reason) {
Order order = orderService.cancelOrder(id, reason);
LOGGER.info("Order with ID: {} cancelled", id);
return ResponseEntity.ok(order);
}



@PutMapping("/status/{id}")
@Operation(
summary = "Update order status",
Expand All @@ -138,4 +157,21 @@ public ResponseEntity<Order> updateOrderStatus(@PathVariable final UUID id,
LOGGER.info("Order status updated for ID: {}", id);
return ResponseEntity.ok(order);
}



@GetMapping("/trackOrder/{id}")
@Operation(
summary = "Track order",
description = "Tracks the location of an order based on "
+ "the provided order ID"
)
@ApiResponse(
responseCode = "200", description = "Order location tracked"
)
public ResponseEntity<OrderLocation> trackOrder(@PathVariable final UUID id) {
OrderLocation location = orderService.trackOrder(id);
LOGGER.info("Order location tracked for ID: {}", id);
return ResponseEntity.ok(location);
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/podzilla/order/dtos/LocationDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.podzilla.order.dtos;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class LocationDTO {
private double first; // Latitude
private double second; // Longitude
}
163 changes: 163 additions & 0 deletions src/main/java/com/podzilla/order/messaging/OrderConsumer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.podzilla.order.messaging;

import com.podzilla.mq.EventsConstants;
import com.podzilla.mq.events.BaseEvent;
import com.podzilla.mq.events.CartCheckedoutEvent;
import com.podzilla.mq.events.OrderAssignedToCourierEvent;
import com.podzilla.mq.events.OrderDeliveredEvent;
import com.podzilla.mq.events.OrderOutForDeliveryEvent;
import com.podzilla.mq.events.OrderPackagedEvent;
import com.podzilla.mq.events.WarehouseOrderFulfillmentFailedEvent;
import com.podzilla.mq.events.WarehouseStockReservedEvent;
import com.podzilla.order.model.Address;
import com.podzilla.order.model.Order;
import com.podzilla.order.model.OrderProduct;
import com.podzilla.order.model.OrderStatus;

import com.podzilla.order.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.UUID;

@Component
@Slf4j
public class OrderConsumer {

private final OrderService orderService;

public OrderConsumer(final OrderService orderService) {
this.orderService = orderService;
}

@RabbitListener(queues = EventsConstants.ORDER_INVENTORY_EVENT_QUEUE)
public void handleStockReserved(final BaseEvent payload) {
if (payload instanceof WarehouseStockReservedEvent) {
WarehouseStockReservedEvent warehouseStockReservedEvent =
(WarehouseStockReservedEvent) payload;
handleWarehouseStockReservedEvent(warehouseStockReservedEvent);
}
if (payload instanceof WarehouseOrderFulfillmentFailedEvent) {
WarehouseOrderFulfillmentFailedEvent warehouseOrderFulfillmentFailedEvent =
(WarehouseOrderFulfillmentFailedEvent) payload;
handleWarehouseOrderFulfillmentFailedEvent(
warehouseOrderFulfillmentFailedEvent);
}
}

@RabbitListener(queues = EventsConstants.ORDER_ORDER_EVENT_QUEUE)
public void trackOrder(final BaseEvent payload) {
if (payload instanceof OrderPackagedEvent) {
OrderPackagedEvent orderPackagedEvent = (OrderPackagedEvent) payload;
handleOrderPackagedEvent(orderPackagedEvent);
}
if (payload instanceof OrderAssignedToCourierEvent) {
OrderAssignedToCourierEvent orderAssignedToCourierEvent =
(OrderAssignedToCourierEvent) payload;
handleOrderAssignedToCourierEvent(orderAssignedToCourierEvent);
}
if (payload instanceof OrderOutForDeliveryEvent) {
OrderOutForDeliveryEvent orderOutForDeliveryEvent =
(OrderOutForDeliveryEvent) payload;
handleOrderOutForDeliveryEvent(orderOutForDeliveryEvent);
}
if (payload instanceof OrderDeliveredEvent) {
OrderDeliveredEvent orderDeliveredEvent =
(OrderDeliveredEvent) payload;
handleOrderDeliveredEvent(orderDeliveredEvent);
}
if (payload instanceof CartCheckedoutEvent) {
CartCheckedoutEvent cartCheckedoutEvent =
(CartCheckedoutEvent) payload;
handleCartCheckoutEvent(cartCheckedoutEvent);
}
}

private void handleWarehouseStockReservedEvent(
final WarehouseStockReservedEvent warehouseStockReservedEvent) {

log.info("✅ Stock reserved for order: {}",
warehouseStockReservedEvent.getOrderId());
orderService.placeOrder(UUID.fromString(warehouseStockReservedEvent.getOrderId()));
}

private void handleWarehouseOrderFulfillmentFailedEvent(
final WarehouseOrderFulfillmentFailedEvent
warehouseOrderFulfillmentFailedEvent) {
log.info("❌ Order fulfillment failed for order: {}, reason: {}",
warehouseOrderFulfillmentFailedEvent.getOrderId(),
warehouseOrderFulfillmentFailedEvent.getReason());
orderService.cancelOrder(
UUID.fromString(warehouseOrderFulfillmentFailedEvent.getOrderId()),
warehouseOrderFulfillmentFailedEvent.getReason());
}

private void handleCartCheckoutEvent(
final CartCheckedoutEvent cartCheckedoutEvent) {
log.info("✅ Cart checked out for user with id: {}",
cartCheckedoutEvent.getCustomerId());
Address address = new Address();
address.setStreet(cartCheckedoutEvent.getDeliveryAddress().getStreet());
address.setCity(cartCheckedoutEvent.getDeliveryAddress().getCity());
address.setState(cartCheckedoutEvent.getDeliveryAddress().getState());
address.setCountry(cartCheckedoutEvent.getDeliveryAddress().getCountry());
address.setPostalCode(cartCheckedoutEvent.getDeliveryAddress().getPostalCode());
List<OrderProduct> orderProducts = cartCheckedoutEvent.getItems().stream()
.map(orderProduct -> {
OrderProduct product = new OrderProduct();
product.setProductId(UUID.fromString(orderProduct.getProductId()));
product.setQuantity(orderProduct.getQuantity());
product.setPricePerUnit(orderProduct.getPricePerUnit());
return product;
}).toList();
Order order = new Order();
order.setUserId(UUID.fromString(cartCheckedoutEvent.getCustomerId()));
order.setTotalAmount(cartCheckedoutEvent.getTotalAmount());
order.setStatus(OrderStatus.PENDING);
order.setShippingAddress(address);
order.setOrderProducts(orderProducts);
order.setConfirmationType(cartCheckedoutEvent.getConfirmationType());
order.setSignature(cartCheckedoutEvent.getSignature());
order.setOrderLatitude(cartCheckedoutEvent.getOrderLatitude());
order.setOrderLongitude(cartCheckedoutEvent.getOrderLongitude());
orderService.createOrder(order);
}

private void handleOrderAssignedToCourierEvent(final OrderAssignedToCourierEvent orderAssignedToCourierEvent) {
log.info("✅ Order assigned to courier for order: {}",
orderAssignedToCourierEvent.getOrderId());
orderService.updateOrder(
UUID.fromString(orderAssignedToCourierEvent.getOrderId()),
Order.builder()
.courierId(UUID.fromString(orderAssignedToCourierEvent.getCourierId()))
.status(OrderStatus.ORDER_ASSIGNED_TO_COURIER)
.build());
}

private void handleOrderDeliveredEvent(
final OrderDeliveredEvent orderDeliveredEvent) {
log.info("✅ Order delivered for order: {}",
orderDeliveredEvent.getOrderId());
orderService.updateOrderStatus(UUID.fromString(orderDeliveredEvent.getOrderId()), OrderStatus.DELIVERED);
}

private void handleOrderOutForDeliveryEvent(final OrderOutForDeliveryEvent orderOutForDeliveryEvent) {
log.info("✅ Order out for delivery for order: {}", orderOutForDeliveryEvent.getOrderId());
orderService.updateOrder(
UUID.fromString(orderOutForDeliveryEvent.getOrderId()),
Order.builder()
.courierId(UUID.fromString(orderOutForDeliveryEvent.getCourierId()))
.status(OrderStatus.OUT_FOR_DELIVERY)
.build());
}

private void handleOrderPackagedEvent(final OrderPackagedEvent orderPackagedEvent) {
log.info("✅ Order packaged for order: {}",
orderPackagedEvent.getOrderId());
orderService.updateOrderStatus(
UUID.fromString(orderPackagedEvent.getOrderId()),
OrderStatus.PACKAGED);
}
}
Loading