Order tracking endpoint for the tracking page
Java · Spring Boot · beginner · modification
Adds the backend for the new order tracking page. `OrderTrackingController` serves `GET /api/orders/{orderId}/tracking`: it loads the order through the existing `findByIdAndCustomerId` (so another customer's order is a plain 404), looks up the order's shipment with a new `ShipmentRepository.findByOrderId`, and returns a `TrackingView` record. `TrackingView.of` copies the order's id and status and, when there is a shipment, the shipment details the page needs; before shipping those stay `null`.
Shop backend on Spring Boot 3.3 (Java 21), Spring Data JPA (Hibernate 6) on PostgreSQL, Spring Security. Every `/api/**` request needs a signed-in session; the principal is a `ShopUser(Long customerId, String email)` record that `@AuthenticationPrincipal` resolves, and `customerId` is never null. Existing and unchanged: the `Order` entity (`id`, `customerId`, `status` stored as an `OrderStatus` enum name: `PLACED`, `PACKED`, `SHIPPED`, `DELIVERED`, `CANCELLED`), `OrderRepository` with `Optional<Order> findByIdAndCustomerId(Long id, Long customerId)`, and the `Shipment` entity (`id`, `orderId`, `carrier`, `trackingNumber`, `estimatedDelivery` as a `LocalDate`). The warehouse creates the shipment row when it buys the label, with all three of `carrier`, `trackingNumber` and `estimatedDelivery` set, and `shipments.order_id` is unique, so an order has at most one shipment. Jackson runs with Spring Boot's defaults, which write a `LocalDate` as `"yyyy-MM-dd"`. A non-numeric `{orderId}` is answered with 400 by Spring MVC before the method runs. The tracking page itself is a separate frontend change.
Requirements
- `GET /api/orders/{orderId}/tracking` returns one JSON object for the signed-in customer's order with five fields: `orderId`, `status` (the order status name, e.g. `"SHIPPED"`), `carrier`, `trackingNumber` and `estimatedDelivery` (an ISO date, `"2026-10-30"`). The tracking page is built against exactly these five fields: it shows the status, the carrier with a link built from the tracking number, and an "Arrives by" line from `estimatedDelivery`.
- Before the order has a shipment, `carrier`, `trackingNumber` and `estimatedDelivery` are `null` (the page then shows "Preparing your order"). Once a shipment exists, all three come from that shipment row.
- A customer only ever sees their own orders: an order that belongs to another customer and an id that does not exist both get the same `404`, so order ids cannot be probed.
Files touched
- src/main/java/com/acme/shop/orders/ShipmentRepository.java
- src/main/java/com/acme/shop/orders/TrackingView.java
- src/main/java/com/acme/shop/orders/OrderTrackingController.java
--- src/main/java/com/acme/shop/orders/ShipmentRepository.java
package com.acme.shop.orders;
+import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ShipmentRepository extends JpaRepository<Shipment, Long> {
+
+ Optional<Shipment> findByOrderId(Long orderId);
}
--- src/main/java/com/acme/shop/orders/TrackingView.java
+package com.acme.shop.orders;
+
+public record TrackingView(
+ Long orderId,
+ OrderStatus status,
+ String carrier,
+ String trackingNumber) {
+
+ public static TrackingView of(Order order, Shipment shipment) {
+ if (shipment == null) {
+ return new TrackingView(order.getId(), order.getStatus(), null, null);
+ }
+ return new TrackingView(
+ order.getId(),
+ order.getStatus(),
+ shipment.getCarrier(),
+ shipment.getTrackingNumber());
+ }