OrderController.java
package io.slayyyyer.orders.web.rest;
import io.slayyyyer.orders.domain.Order;
import io.slayyyyer.orders.domain.OrderStatus;
import io.slayyyyer.orders.repository.OrderPage;
import io.slayyyyer.orders.service.CreatedOrder;
import io.slayyyyer.orders.service.OrderService;
import io.slayyyyer.orders.web.dto.CreateOrderRequest;
import io.slayyyyer.orders.web.dto.OrderResponse;
import io.slayyyyer.orders.web.dto.PagedResponse;
import jakarta.validation.Valid;
import java.net.URI;
import java.util.Locale;
import java.util.UUID;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
private final OrderService orders;
public OrderController(OrderService orders) {
this.orders = orders;
}
/**
* Idempotent creation. The first request returns 201 with a Location header; a replay
* with the same key returns 200 and the original order.
*/
@PostMapping
public ResponseEntity<OrderResponse> create(
@RequestHeader(value = "Idempotency-Key", required = false) String idempotencyKey,
@Valid @RequestBody CreateOrderRequest request) {
CreatedOrder created = orders.create(request.toCommand(idempotencyKey));
Order order = created.order();
if (created.replayed()) {
return ResponseEntity.ok()
.header("Idempotent-Replay", "true")
.body(OrderResponse.from(order));
}
return ResponseEntity.created(URI.create("/api/orders/" + order.id()))
.body(OrderResponse.from(order));
}
@GetMapping("/{id}")
public OrderResponse get(@PathVariable UUID id) {
return OrderResponse.from(orders.require(id));
}
@GetMapping
public PagedResponse<OrderResponse> byCustomer(
@RequestParam UUID customerId,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
OrderPage result = orders.byCustomer(customerId, parseStatus(status), page, size);
return PagedResponse.of(
result.orders().stream().map(OrderResponse::from).toList(),
result.page(),
result.size(),
result.totalCount(),
result.hasNextPage());
}
@PostMapping("/{id}/status")
public OrderResponse transition(@PathVariable UUID id, @RequestParam String status) {
return OrderResponse.from(orders.transition(id, parseStatus(status)));
}
static OrderStatus parseStatus(String status) {
if (status == null || status.isBlank()) {
return null;
}
try {
return OrderStatus.valueOf(status.trim().toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("unknown order status: " + status);
}
}
static HttpStatus noContent() {
return HttpStatus.NO_CONTENT;
}
static HttpHeaders headers() {
return new HttpHeaders();
}
}