feat: add POST /api/orders endpoint with validation
- Create CreateOrderRequest DTO with jakarta.validation annotations - Validate plate format (ABC123 or ABC12A) via @Pattern regex - Validate letter text: @NotBlank, @Size(min=1, max=1000) - Validate template name: optional, @Size(max=50) - Add POST /api/orders endpoint to OrderController (auth required) - Return 201 Created with OrderResponse on success - Add 5 controller tests: no auth (403), create success, invalid plate, empty text, text over 1000 chars - All messages in Swedish (Ogiltigt registreringsnummer, Brevtext krävs, etc.)
This commit is contained in:
parent
0c62d7e60a
commit
55f0fd8771
3 changed files with 109 additions and 0 deletions
|
|
@ -1,12 +1,17 @@
|
|||
package se.bilhalsning.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import se.bilhalsning.dto.CreateOrderRequest;
|
||||
import se.bilhalsning.dto.OrderResponse;
|
||||
import se.bilhalsning.entity.Order;
|
||||
import se.bilhalsning.entity.User;
|
||||
|
|
@ -36,6 +41,23 @@ public class OrderController {
|
|||
return ResponseEntity.ok(orders);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<OrderResponse> create(
|
||||
@Valid @RequestBody CreateOrderRequest request,
|
||||
@AuthenticationPrincipal UserDetails userDetails) {
|
||||
User user = userService.findByEmail(userDetails.getUsername())
|
||||
.orElseThrow(InvalidCredentialsException::new);
|
||||
|
||||
Order order = orderService.createOrder(
|
||||
user.getId(),
|
||||
request.plate(),
|
||||
request.template(),
|
||||
request.letterText()
|
||||
);
|
||||
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(toResponse(order));
|
||||
}
|
||||
|
||||
private OrderResponse toResponse(Order order) {
|
||||
return new OrderResponse(
|
||||
order.getId(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package se.bilhalsning.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public record CreateOrderRequest(
|
||||
@NotBlank(message = "Registreringsnummer krävs")
|
||||
@Pattern(regexp = "^[A-Za-z]{3}\\d{2}[A-Za-z0-9]$", message = "Ogiltigt registreringsnummer")
|
||||
String plate,
|
||||
|
||||
@Size(max = 50, message = "Mallnamn får vara max 50 tecken")
|
||||
String template,
|
||||
|
||||
@NotBlank(message = "Brevtext krävs")
|
||||
@Size(min = 1, max = 1000, message = "Brevtexten måste vara mellan 1 och 1000 tecken")
|
||||
String letterText
|
||||
) {}
|
||||
|
|
@ -2,6 +2,7 @@ package se.bilhalsning.controller;
|
|||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
|
|
@ -101,4 +102,72 @@ class OrderControllerTest {
|
|||
mockMvc.perform(get("/api/orders"))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturn403WhenPostingWithoutAuth() throws Exception {
|
||||
mockMvc.perform(post("/api/orders")
|
||||
.contentType("application/json")
|
||||
.content("{\"plate\":\"ABC123\",\"letterText\":\"Hej\"}"))
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "test@bilhalsning.se")
|
||||
void shouldCreateOrderSuccessfully() throws Exception {
|
||||
UUID userId = UUID.fromString("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11");
|
||||
User user = new User();
|
||||
user.setId(userId);
|
||||
user.setEmail("test@bilhalsning.se");
|
||||
|
||||
when(userService.findByEmail("test@bilhalsning.se")).thenReturn(Optional.of(user));
|
||||
|
||||
se.bilhalsning.entity.Order savedOrder = new se.bilhalsning.entity.Order();
|
||||
savedOrder.setId(UUID.fromString("d1eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"));
|
||||
savedOrder.setUserId(userId);
|
||||
savedOrder.setPlate("ABC123");
|
||||
savedOrder.setTemplate("Komplimang");
|
||||
savedOrder.setLetterText("Hej fin bil!");
|
||||
savedOrder.setStatus(se.bilhalsning.entity.OrderStatus.PENDING_PAYMENT);
|
||||
|
||||
when(orderService.createOrder(userId, "ABC123", "Komplimang", "Hej fin bil!"))
|
||||
.thenReturn(savedOrder);
|
||||
|
||||
mockMvc.perform(post("/api/orders")
|
||||
.contentType("application/json")
|
||||
.content("{\"plate\":\"ABC123\",\"template\":\"Komplimang\",\"letterText\":\"Hej fin bil!\"}"))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.id").value("d1eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))
|
||||
.andExpect(jsonPath("$.plate").value("ABC123"))
|
||||
.andExpect(jsonPath("$.template").value("Komplimang"))
|
||||
.andExpect(jsonPath("$.status").value("pending_payment"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "test@bilhalsning.se")
|
||||
void shouldRejectInvalidPlateFormat() throws Exception {
|
||||
mockMvc.perform(post("/api/orders")
|
||||
.contentType("application/json")
|
||||
.content("{\"plate\":\"INVALID\",\"letterText\":\"Hej\"}"))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("Ogiltigt registreringsnummer")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "test@bilhalsning.se")
|
||||
void shouldRejectEmptyLetterText() throws Exception {
|
||||
mockMvc.perform(post("/api/orders")
|
||||
.contentType("application/json")
|
||||
.content("{\"plate\":\"ABC123\",\"letterText\":\"\"}"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(username = "test@bilhalsning.se")
|
||||
void shouldRejectLetterTextOver1000Chars() throws Exception {
|
||||
String longText = "a".repeat(1001);
|
||||
mockMvc.perform(post("/api/orders")
|
||||
.contentType("application/json")
|
||||
.content("{\"plate\":\"ABC123\",\"letterText\":\"" + longText + "\"}"))
|
||||
.andExpect(status().isBadRequest());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue