Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.Podzilla</groupId>
<artifactId>podzilla-utils-lib</artifactId>
<version>1.1.11</version>

</dependency>

<!-- Swagger / OpenAPI -->
<dependency>
Expand Down Expand Up @@ -150,7 +156,12 @@
<scope>test</scope>
</dependency>
</dependencies>

<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>

<build>
<plugins>
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/cart/CartApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;

@SpringBootApplication
@EnableMongoRepositories(basePackages = "cart.repository")
@ComponentScan(basePackages = { "com.podzilla" })
public class CartApplication {
public static void main(final String[] args) {
SpringApplication.run(CartApplication.class, args);
Expand Down
78 changes: 48 additions & 30 deletions src/main/java/cart/controller/CartController.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package cart.controller;



import cart.model.Cart;
import cart.service.CartService;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -13,6 +11,9 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.podzilla.mq.events.DeliveryAddress;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -22,6 +23,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import cart.model.CartItem;
import io.swagger.v3.oas.annotations.media.Content;
import org.springframework.web.bind.annotation.RequestHeader;

@RestController
@RequestMapping("/api/carts")
Expand All @@ -46,9 +48,11 @@ public class CartController {
description = "Internal server error",
content = @Content)
})
@PostMapping("/create/{customerId}")


@PostMapping("/create")
public ResponseEntity<Cart> createCart(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering createCart endpoint"
+ " with customerId:", customerId);
Cart cart = cartService.createCart(customerId);
Expand All @@ -63,9 +67,10 @@ public ResponseEntity<Cart> createCart(
@ApiResponse(responseCode = "404",
description = "Cart not found for this customer")
})
@GetMapping("/customer/{customerId}")

@GetMapping("/customer")
public ResponseEntity<Cart> getCartByCustomerId(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering getCartByCustomerId"
+ " endpoint with customerId:",
customerId);
Expand All @@ -81,9 +86,10 @@ public ResponseEntity<Cart> getCartByCustomerId(
@ApiResponse(responseCode = "404",
description = "Cart not found")
})
@DeleteMapping("/customer/{customerId}")

@DeleteMapping("/customer")
public ResponseEntity<Void> deleteCart(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering deleteCart end"
+ "point with customerId:", customerId);
cartService.deleteCartByCustomerId(customerId);
Expand All @@ -105,7 +111,7 @@ public ResponseEntity<Void> deleteCart(
})
@PostMapping("/{customerId}/items")
public ResponseEntity<Cart> addItemToCart(
@PathVariable("customerId") final String customerId,
@RequestHeader("X-User-Id") final String customerId,
@RequestBody final CartItem cartItem) {
log.debug("Entering addItemToCart"
+ " endpoint with customerId: {},"
Expand All @@ -127,7 +133,7 @@ public ResponseEntity<Cart> addItemToCart(
})
@PatchMapping("/{customerId}/items/{productId}")
public ResponseEntity<Cart> updateItemQuantity(
@PathVariable("customerId") final String customerId,
@RequestHeader("X-User-Id") final String customerId,
@PathVariable("productId") final String productId,
@RequestParam final int quantity) {
log.debug("Entering updateItemQuantity"
Expand All @@ -150,7 +156,7 @@ public ResponseEntity<Cart> updateItemQuantity(
})
@DeleteMapping("/{customerId}/items/{productId}")
public ResponseEntity<Cart> removeItemFromCart(
@PathVariable("customerId") final String customerId,
@RequestHeader("X-User-Id") final String customerId,
@PathVariable("productId") final String productId) {
log.debug("Entering removeItemFromCart"
+ " endpoint with customerId:,"
Expand All @@ -169,9 +175,10 @@ public ResponseEntity<Cart> removeItemFromCart(
@ApiResponse(responseCode = "404",
description = "Cart not found")
})
@DeleteMapping("/{customerId}/clear")

@DeleteMapping("/clear")
public ResponseEntity<Void> clearCart(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering clearCart"
+ " endpoint with customerId:", customerId);
cartService.clearCart(customerId);
Expand All @@ -186,9 +193,10 @@ public ResponseEntity<Void> clearCart(
@ApiResponse(responseCode = "404",
description = "Cart not found")
})
@PatchMapping("/{customerId}/archive")

@PatchMapping("/archive")
public ResponseEntity<Cart> archiveCart(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering archiveCart"
+ " endpoint with customerId:", customerId);
Cart archivedCart = cartService.archiveCart(customerId);
Expand All @@ -203,9 +211,10 @@ public ResponseEntity<Cart> archiveCart(
@ApiResponse(responseCode = "404",
description = "Archived cart not found")
})
@PatchMapping("/{customerId}/unarchive")
// Replace @PatchMapping("/{customerId}/unarchive")
@PatchMapping("/unarchive")
public ResponseEntity<Cart> unarchiveCart(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering unarchiveCart"
+ " endpoint with customerId:", customerId);
Cart activeCart = cartService.unarchiveCart(customerId);
Expand All @@ -222,20 +231,27 @@ public ResponseEntity<Cart> unarchiveCart(
@ApiResponse(responseCode = "500",
description = "Failed to communicate with Order Service")
})
@PostMapping("/{customerId}/checkout")

@PostMapping("/checkout")
public ResponseEntity<Cart> checkoutCart(
@PathVariable("customerId") final String customerId) {
log.debug("Entering checkoutCart"
+ " endpoint with customerId:", customerId);
@RequestHeader("X-User-Id") final String customerId,
@RequestParam(required = true) final com.podzilla.mq.events.ConfirmationType confirmationType,
@RequestParam(required = false) final String signature,
@RequestParam(required = true) final Double longitude,
@RequestParam(required = true) final Double latitude,
@RequestParam(required = true) final DeliveryAddress address
) {
log.debug("Entering checkoutCart endpoint with customerId: {}," +
" confirmationType: {}, signature: {}",
customerId, confirmationType, signature);
try {
Cart updatedCart = cartService.checkoutCart(customerId);
Cart updatedCart = cartService.checkoutCart(customerId, confirmationType,
signature, longitude, latitude, address);
log.debug("Cart checked out: {}", updatedCart);
return ResponseEntity.ok(updatedCart);
} catch (Exception ex) {
log.error("Error during checkout"
+ " for customerId: {}", customerId, ex);
throw new IllegalCallerException("Error "
+ "communicating with Order Service");
log.error("Error during checkout for customerId: {}", customerId, ex);
throw new IllegalCallerException("Error communicating with Order Service");
}
}

Expand All @@ -248,9 +264,10 @@ public ResponseEntity<Cart> checkoutCart(
@ApiResponse(responseCode = "404",
description = "Active cart not found")
})
@PostMapping("/{customerId}/promo/{promoCode}")

@PostMapping("/promo/{promoCode}")
public ResponseEntity<Cart> applyPromoCode(
@PathVariable("customerId") final String customerId,
@RequestHeader("X-User-Id") final String customerId,
@PathVariable("promoCode") final String promoCode) {
log.debug("Entering applyPromoCode endpoint with "
+ "customerId: {}, promoCode: {}",
Expand All @@ -269,9 +286,10 @@ public ResponseEntity<Cart> applyPromoCode(
@ApiResponse(responseCode = "404",
description = "Active cart not found")
})
@DeleteMapping("/{customerId}/promo")

@DeleteMapping("/promo")
public ResponseEntity<Cart> removePromoCode(
@PathVariable("customerId") final String customerId) {
@RequestHeader("X-User-Id") final String customerId) {
log.debug("Entering removePromoCode "
+ "endpoint with customerId: {}", customerId);
Cart updatedCart = cartService.removePromoCode(customerId);
Expand Down
24 changes: 0 additions & 24 deletions src/main/java/cart/model/OrderRequest.java

This file was deleted.

70 changes: 43 additions & 27 deletions src/main/java/cart/service/CartService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,41 @@
import cart.exception.GlobalHandlerException;
import cart.model.Cart;
import cart.model.CartItem;
import cart.model.OrderRequest;
import cart.model.PromoCode;
import cart.repository.CartRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import com.podzilla.mq.EventPublisher;
import com.podzilla.mq.EventsConstants;
import com.podzilla.mq.events.CartCheckedoutEvent;
import com.podzilla.mq.events.ConfirmationType;
import com.podzilla.mq.events.DeliveryAddress;
import com.podzilla.mq.events.OrderItem;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartService {

private final CartRepository cartRepository;
private final RabbitTemplate rabbitTemplate;
private final EventPublisher eventPublisher;
private final PromoCodeService promoCodeService;

@Value("${rabbitmq.exchange.name}")
private String exchangeName;
@Value("${rabbitmq.routing.key.checkout}")
private String checkoutRoutingKey;

public CartService(final CartRepository cartRepository,
final RabbitTemplate rabbitTemplate,
final PromoCodeService promoCodeService) {
final EventPublisher eventPublisher,
final PromoCodeService promoCodeService) {
this.cartRepository = cartRepository;
this.rabbitTemplate = rabbitTemplate;
this.eventPublisher = eventPublisher;
this.promoCodeService = promoCodeService;
}

Expand Down Expand Up @@ -250,8 +251,10 @@ private BigDecimal calculateSubTotal(final Cart cart) {
.reduce(BigDecimal.ZERO, BigDecimal::add);
}

public Cart checkoutCart(final String customerId) {
log.debug("Entering checkoutCart [RabbitMQ] for customerId: {}", customerId);
public Cart checkoutCart(final String customerId, final ConfirmationType confirmationType,
final String signature, final Double longitude, final Double latitude, DeliveryAddress address) {
log.debug("Entering checkoutCart for customerId: {} with confirmationType: {}",
customerId, confirmationType);
Cart cart = getActiveCart(customerId);

recalculateCartTotals(cart);
Expand All @@ -261,21 +264,34 @@ public Cart checkoutCart(final String customerId) {
throw new GlobalHandlerException(HttpStatus.BAD_REQUEST, "Cannot checkout an empty cart.");
}

OrderRequest checkoutEvent = new OrderRequest(
UUID.randomUUID().toString(),
customerId,
cart.getId(),
new ArrayList<>(cart.getItems()),
cart.getSubTotal(),
cart.getDiscountAmount(),
cart.getTotalPrice(),
cart.getAppliedPromoCode()
);
if (confirmationType == ConfirmationType.SIGNATURE && (signature == null || signature.trim().isEmpty())) {
throw new GlobalHandlerException(HttpStatus.BAD_REQUEST, "Signature is required for SIGNATURE confirmation type");
}

List<OrderItem> orderItems = cart.getItems().stream()
.map(cartItem -> OrderItem.builder()
.productId(cartItem.getProductId())
.quantity(cartItem.getQuantity())
.pricePerUnit(cartItem.getUnitPrice())
.build())
.collect(Collectors.toList());

CartCheckedoutEvent checkoutEvent = CartCheckedoutEvent.builder()
.cartId(cart.getId())
.customerId(customerId)
.items(orderItems)
.totalAmount(cart.getTotalPrice())
.deliveryAddress(address)
.orderLatitude(latitude != null ? latitude : 0.0)
.orderLongitude(longitude != null ? longitude : 0.0)
.signature(signature)
.confirmationType(confirmationType)
.build();

try {
log.debug("Publishing checkout event for cartId: {} with totals: Sub={}, Discount={}, Total={}",
cart.getId(), cart.getSubTotal(), cart.getDiscountAmount(), cart.getTotalPrice());
rabbitTemplate.convertAndSend(exchangeName, checkoutRoutingKey, checkoutEvent);
log.debug("Publishing checkout event for cartId: {} with totals: Sub={}, Discount={}, Total={}, ConfirmationType={}",
cart.getId(), cart.getSubTotal(), cart.getDiscountAmount(), cart.getTotalPrice(), confirmationType);
eventPublisher.publishEvent(EventsConstants.ORDER_PLACED, checkoutEvent);

log.info("Checkout event published successfully for cartId: {}. Clearing cart.", cart.getId());
cart.getItems().clear();
Expand Down
Loading