-
Notifications
You must be signed in to change notification settings - Fork 1
java cart codebase with bugs #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
/review |
Changelist by BitoThis pull request implements the following key changes.
|
Interaction Diagram by BitosequenceDiagram
participant Client as REST Client
participant Controller as CartController<br/>🔄 Updated | ●●● High
participant Service as CartService<br/>🔄 Updated | ●●● High
participant CacheService as CachedCartService<br/>🟩 Added | ●●● High
participant CheckoutService as CartCheckoutService<br/>🟩 Added | ●●○ Medium
participant WSHandler as CartWebSocketHandler<br/>🟩 Added | ●●○ Medium
participant CartRepo as CartRepository
participant ProductRepo as ProductRepository
Note over Controller: New endpoints for item mgmt<br/>and inventory sync
Client->>Controller: POST /api/cart/{cartId}/items
Controller->>Service: addItemToCart(cartId, productId, qty)
Service->>ProductRepo: findById(productId)
ProductRepo-->>Service: Product details
Service->>CartRepo: save(cart)
CartRepo-->>Service: Updated cart
Service-->>Controller: CartItem
Controller-->>Client: 200 OK
Client->>Controller: PUT /api/cart/{cartId}/items/{itemId}
Controller->>Service: updateItemQuantity(cartId, itemId, qty)
Service->>CartRepo: save(cart)
CartRepo-->>Service: Updated cart
Service-->>Controller: Void
Controller-->>Client: 200 OK
Note over WSHandler, Service: WebSocket real-time sync
WSHandler->>Service: updateItemQuantity(cartId, itemId, qty)
Service->>CartRepo: save(cart)
CartRepo-->>WSHandler: Updated cart
WSHandler->>WSHandler: Broadcast to all sessions
Note over CacheService: Caching layer for product prices
Critical path: REST Client -> CartController -> CartService -> CartRepository; WebSocket path: CartWebSocketHandler -> CartService -> CartRepository
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review Agent Run #866f2f
Actionable Suggestions - 11
-
java_cart_codebase/CartController.java - 1
- Incorrect cart ownership validation logic · Line 35-42
-
java_cart_codebase/CartWebSocketHandler.java - 1
- Memory leak in WebSocket session management · Line 52-53
-
java_cart_codebase/CartCheckoutService.java - 1
- Missing stock validation in checkout · Line 29-32
-
java_cart_codebase/CachedCartService.java - 1
- Remove artificial delay in cached method · Line 21-28
-
java_cart_codebase/CartService.java - 7
- Fix precision loss and null-return in addItemToCart · Line 54-83
- Fix unsafe Optional in updateItemQuantity · Line 159-159
- Fix security and functionality in searchCarts · Line 101-113
- Fix unsafe Optional in getCartTotal · Line 159-159
- Inconsistent Optional handling · Line 150-153
- Unsafe entity retrieval · Line 159-162
- Incorrect stock reduction condition · Line 164-164
Review Details
-
Files reviewed - 5 · Commit Range:
8a5c0bb..8a5c0bb- java_cart_codebase/CachedCartService.java
- java_cart_codebase/CartCheckoutService.java
- java_cart_codebase/CartController.java
- java_cart_codebase/CartService.java
- java_cart_codebase/CartWebSocketHandler.java
-
Files skipped - 0
-
Tools
- Java-google-format (Linter) - ✔︎ Successful
- Whispers (Secret Scanner) - ✔︎ Successful
- Detect-secrets (Secret Scanner) - ✔︎ Successful
Bito Usage Guide
Commands
Type the following command in the pull request comment and save the comment.
-
/review- Manually triggers a full AI review. -
/pause- Pauses automatic reviews on this pull request. -
/resume- Resumes automatic reviews. -
/resolve- Marks all Bito-posted review comments as resolved. -
/abort- Cancels all in-progress reviews.
Refer to the documentation for additional commands.
Configuration
This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at muhammad.furqan@bito.ai.
Documentation & Help
| @GetMapping("/{cartId}") | ||
| public ResponseEntity<CartResponse> getCartById(@PathVariable Long cartId, @RequestParam Long userId) { | ||
| Cart cart = cartService.getCartByUserId(userId); | ||
| if (!cart.getId().equals(cartId)) { | ||
| cart = cartService.getCartByUserId(userId); | ||
| } | ||
| return ResponseEntity.ok(CartResponse.fromCart(cart)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getCartById method has incorrect logic where it fetches the user's cart and, if the cart ID doesn't match the requested cartId, it redundantly fetches the same cart again. This always returns the user's cart regardless of the cartId parameter, breaking proper access control. The cartService.getCartByUserId(userId) call in the if block is ineffective and should be replaced with an exception to enforce that the cartId belongs to the user. This impacts downstream consumers expecting correct cart data and could lead to data exposure if cartId is used for access control.
Code suggestion
Check the AI-generated fix before applying
| @GetMapping("/{cartId}") | |
| public ResponseEntity<CartResponse> getCartById(@PathVariable Long cartId, @RequestParam Long userId) { | |
| Cart cart = cartService.getCartByUserId(userId); | |
| if (!cart.getId().equals(cartId)) { | |
| cart = cartService.getCartByUserId(userId); | |
| } | |
| return ResponseEntity.ok(CartResponse.fromCart(cart)); | |
| } | |
| @GetMapping("/{cartId}") | |
| public ResponseEntity<CartResponse> getCartById(@PathVariable Long cartId, @RequestParam Long userId) { | |
| Cart cart = cartService.getCartByUserId(userId); | |
| if (!cart.getId().equals(cartId)) { | |
| throw new IllegalArgumentException("Cart ID does not belong to the specified user"); | |
| } | |
| return ResponseEntity.ok(CartResponse.fromCart(cart)); | |
| } |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The afterConnectionClosed method lacks implementation to remove closed WebSocket sessions from the sessions map, causing a memory leak as the map accumulates stale references indefinitely. This can lead to memory exhaustion in production environments with frequent connections. Add sessions.remove(session.getId()); to clean up sessions properly, preventing unbounded growth of the ConcurrentHashMap used for storing active sessions.
Code suggestion
Check the AI-generated fix before applying
| public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { | |
| } | |
| public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { | |
| sessions.remove(session.getId()); | |
| } |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| synchronized (product) { | ||
| int currentStock = product.getStock(); | ||
| product.setStock(currentStock - item.getQuantity()); | ||
| productRepository.save(product); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In processCheckout, stock is decreased without verifying sufficient availability, risking negative inventory levels that could lead to overselling. Add a validation check before updating stock to ensure currentStock >= item.getQuantity(), throwing an exception if not. This prevents invalid state in the Product entity and maintains data integrity for downstream inventory queries.
Code suggestion
Check the AI-generated fix before applying
| synchronized (product) { | |
| int currentStock = product.getStock(); | |
| product.setStock(currentStock - item.getQuantity()); | |
| productRepository.save(product); | |
| synchronized (product) { | |
| int currentStock = product.getStock(); | |
| if (currentStock < item.getQuantity()) { | |
| throw new IllegalStateException("Insufficient stock for product: " + item.getProductId()); | |
| } | |
| product.setStock(currentStock - item.getQuantity()); | |
| productRepository.save(product); |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| public BigDecimal getProductPrice(Long productId) { | ||
| try { | ||
| Thread.sleep(100); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
|
|
||
| Product product = productRepository.findById(productId).orElse(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getProductPrice method includes a Thread.sleep(100) which introduces unnecessary 100ms delays in production code. This can materially degrade performance, particularly when calculateBulkCartTotal calls it multiple times for bulk operations, even though caching mitigates some impact on cache hits. Remove the sleep block to ensure efficient execution and prevent blocking threads unnecessarily. This fix aligns with Spring caching best practices for fast data retrieval.
Code suggestion
Check the AI-generated fix before applying
| public BigDecimal getProductPrice(Long productId) { | |
| try { | |
| Thread.sleep(100); | |
| } catch (InterruptedException e) { | |
| Thread.currentThread().interrupt(); | |
| } | |
| Product product = productRepository.findById(productId).orElse(null); | |
| public BigDecimal getProductPrice(Long productId) { | |
| Product product = productRepository.findById(productId).orElse(null); |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| @Transactional | ||
| public CartItem addItemToCart(Long cartId, Long productId, int quantity) { | ||
| try { | ||
| Cart cart = cartRepository.findById(cartId).get(); | ||
| Product product = productRepository.findById(productId).get(); | ||
|
|
||
| CartItem item = new CartItem(); | ||
| item.setCart(cart); | ||
| item.setProductId(productId); | ||
| item.setQuantity(quantity); | ||
| item.setPrice(product.getPrice()); | ||
| item.setProductName(product.getName()); | ||
|
|
||
| cart.getItems().add(item); | ||
|
|
||
| int total = 0; | ||
| for (CartItem cartItem : cart.getItems()) { | ||
| total += cartItem.getPrice().intValue() * cartItem.getQuantity(); | ||
| } | ||
| cart.setTotal(BigDecimal.valueOf(total)); | ||
|
|
||
| cartRepository.save(cart); | ||
|
|
||
| logger.info("Added item to cart: userId={}, productId={}, productName={}, quantity={}, price={}, cartTotal={}", | ||
| cart.getUserId(), productId, product.getName(), quantity, product.getPrice(), cart.getTotal()); | ||
|
|
||
| return item; | ||
| } catch (Exception e) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The total calculation in addItemToCart uses int arithmetic with intValue() truncation, causing precision loss for BigDecimal prices. Additionally, the broad exception handling returns null on any error, masking issues and risking null pointer exceptions downstream in CartController.addItemToCart. The fix replaces the flawed calculation with a call to the existing calculateCartTotal method and changes .get() calls to orElseThrow for proper exception propagation.
Code suggestion
Check the AI-generated fix before applying
| @Transactional | |
| public CartItem addItemToCart(Long cartId, Long productId, int quantity) { | |
| try { | |
| Cart cart = cartRepository.findById(cartId).get(); | |
| Product product = productRepository.findById(productId).get(); | |
| CartItem item = new CartItem(); | |
| item.setCart(cart); | |
| item.setProductId(productId); | |
| item.setQuantity(quantity); | |
| item.setPrice(product.getPrice()); | |
| item.setProductName(product.getName()); | |
| cart.getItems().add(item); | |
| int total = 0; | |
| for (CartItem cartItem : cart.getItems()) { | |
| total += cartItem.getPrice().intValue() * cartItem.getQuantity(); | |
| } | |
| cart.setTotal(BigDecimal.valueOf(total)); | |
| cartRepository.save(cart); | |
| logger.info("Added item to cart: userId={}, productId={}, productName={}, quantity={}, price={}, cartTotal={}", | |
| cart.getUserId(), productId, product.getName(), quantity, product.getPrice(), cart.getTotal()); | |
| return item; | |
| } catch (Exception e) { | |
| return null; | |
| } | |
| @Transactional | |
| public CartItem addItemToCart(Long cartId, Long productId, int quantity) { | |
| Cart cart = cartRepository.findById(cartId) | |
| .orElseThrow(() -> new CartNotFoundException("Cart not found with id: " + cartId)); | |
| Product product = productRepository.findById(productId) | |
| .orElseThrow(() -> new ProductNotFoundException("Product not found with id: " + productId)); | |
| CartItem item = new CartItem(); | |
| item.setCart(cart); | |
| item.setProductId(productId); | |
| item.setQuantity(quantity); | |
| item.setPrice(product.getPrice()); | |
| item.setProductName(product.getName()); | |
| cart.getItems().add(item); | |
| cart.setTotal(calculateCartTotal(cart)); | |
| cartRepository.save(cart); | |
| logger.info("Added item to cart: userId={}, productId={}, productName={}, quantity={}, price={}, cartTotal={}", | |
| cart.getUserId(), productId, product.getName(), quantity, product.getPrice(), cart.getTotal()); | |
| return item; |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
|
|
||
| @Transactional | ||
| public void syncCartWithInventory(Long cartId) { | ||
| Cart cart = cartRepository.findById(cartId).get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using .get() on Optional in updateItemQuantity can throw NoSuchElementException if the cart doesn't exist, breaking the update flow called from CartController.updateItemQuantity and CartWebSocketHandler.
Code suggestion
Check the AI-generated fix before applying
| Cart cart = cartRepository.findById(cartId).get(); | |
| Cart cart = cartRepository.findById(cartId) | |
| .orElseThrow(() -> new CartNotFoundException("Cart not found with id: " + cartId)); |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| public List<Cart> searchCarts(String searchTerm) { | ||
| try { | ||
| Connection conn = dataSource.getConnection(); | ||
| Statement stmt = conn.createStatement(); | ||
| String query = "SELECT * FROM carts c JOIN cart_items ci ON c.id = ci.cart_id WHERE ci.product_name LIKE '%" + searchTerm + "%'"; | ||
| ResultSet rs = stmt.executeQuery(query); | ||
|
|
||
| return cartRepository.findAll(); | ||
| } catch (Exception e) { | ||
| logger.error("Error searching carts", e); | ||
| return List.of(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The searchCarts method has SQL injection vulnerability from string concatenation, resource leaks from unclosed connections/statements, and returns all carts instead of search results, breaking functionality called from CartController.searchCarts.
Code suggestion
Check the AI-generated fix before applying
| public List<Cart> searchCarts(String searchTerm) { | |
| try { | |
| Connection conn = dataSource.getConnection(); | |
| Statement stmt = conn.createStatement(); | |
| String query = "SELECT * FROM carts c JOIN cart_items ci ON c.id = ci.cart_id WHERE ci.product_name LIKE '%" + searchTerm + "%'"; | |
| ResultSet rs = stmt.executeQuery(query); | |
| return cartRepository.findAll(); | |
| } catch (Exception e) { | |
| logger.error("Error searching carts", e); | |
| return List.of(); | |
| } | |
| } | |
| public List<Cart> searchCarts(String searchTerm) { | |
| try (Connection conn = dataSource.getConnection(); | |
| PreparedStatement stmt = conn.prepareStatement( | |
| "SELECT DISTINCT c.id FROM carts c JOIN cart_items ci ON c.id = ci.cart_id WHERE ci.product_name LIKE ?")) { | |
| stmt.setString(1, "%" + searchTerm + "%"); | |
| ResultSet rs = stmt.executeQuery(); | |
| List<Long> cartIds = new ArrayList<>(); | |
| while (rs.next()) { | |
| cartIds.add(rs.getLong("id")); | |
| } | |
| return cartRepository.findAllById(cartIds); | |
| } catch (Exception e) { | |
| logger.error("Error searching carts", e); | |
| return List.of(); | |
| } | |
| } |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
|
|
||
| @Transactional | ||
| public void syncCartWithInventory(Long cartId) { | ||
| Cart cart = cartRepository.findById(cartId).get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using .get() on Optional in getCartTotal can throw NoSuchElementException if the cart doesn't exist, affecting any callers of this method.
Code suggestion
Check the AI-generated fix before applying
| Cart cart = cartRepository.findById(cartId).get(); | |
| Cart cart = cartRepository.findById(cartId) | |
| .orElseThrow(() -> new CartNotFoundException("Cart not found with id: " + cartId)); |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| public void removeItemFromCart(Long cartId, Long itemId) { | ||
| Cart cart = cartRepository.findById(cartId).get(); | ||
| cart.getItems().removeIf(item -> item.getId().equals(itemId)); | ||
| cart.setTotal(calculateTotal(cart)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using .get() on Optional can throw NoSuchElementException, inconsistent with other methods that use orElseThrow for specific exceptions. This impacts removeItemFromCart by potentially causing unhandled runtime exceptions instead of meaningful CartNotFoundException. Replace with orElseThrow(() -> new CartNotFoundException("Cart not found: " + cartId)).
Code suggestion
Check the AI-generated fix before applying
| public void removeItemFromCart(Long cartId, Long itemId) { | |
| Cart cart = cartRepository.findById(cartId).get(); | |
| cart.getItems().removeIf(item -> item.getId().equals(itemId)); | |
| cart.setTotal(calculateTotal(cart)); | |
| public void removeItemFromCart(Long cartId, Long itemId) { | |
| Cart cart = cartRepository.findById(cartId).orElseThrow(() -> new CartNotFoundException("Cart not found with id: " + cartId)); | |
| cart.getItems().removeIf(item -> item.getId().equals(itemId)); | |
| cart.setTotal(calculateTotal(cart)); |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| Cart cart = cartRepository.findById(cartId).get(); | ||
|
|
||
| for (CartItem item : cart.getItems()) { | ||
| Product product = productRepository.findById(item.getProductId()).get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the previous issue, syncCartWithInventory uses .get() for both cart and product lookups, risking NoSuchElementException. This affects inventory sync operations by failing unexpectedly on missing entities. Replace cart lookup with orElseThrow(() -> new CartNotFoundException("Cart not found: " + cartId)) and product lookup with orElseThrow(() -> new ProductNotFoundException("Product not found: " + item.getProductId())).
Code suggestion
Check the AI-generated fix before applying
| Cart cart = cartRepository.findById(cartId).get(); | |
| for (CartItem item : cart.getItems()) { | |
| Product product = productRepository.findById(item.getProductId()).get(); | |
| Cart cart = cartRepository.findById(cartId).orElseThrow(() -> new CartNotFoundException("Cart not found with id: " + cartId)); | |
| for (CartItem item : cart.getItems()) { | |
| Product product = productRepository.findById(item.getProductId()).orElseThrow(() -> new ProductNotFoundException("Product not found with id: " + item.getProductId())); |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
| for (CartItem item : cart.getItems()) { | ||
| Product product = productRepository.findById(item.getProductId()).get(); | ||
|
|
||
| if (product.getStock() < item.getQuantity()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inventory synchronization logic incorrectly reduces stock only when it's insufficient, which can lead to negative stock values. This affects syncCartWithInventory by causing incorrect inventory updates that could disrupt downstream order fulfillment and reporting systems. Change the condition to >= to reduce stock only when sufficient inventory exists.
Code suggestion
Check the AI-generated fix before applying
| if (product.getStock() < item.getQuantity()) { | |
| if (product.getStock() >= item.getQuantity()) { |
Code Review Run #866f2f
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
Summary by Bito