From fb4d4cf39e12821b881db304cbc249d12732e228 Mon Sep 17 00:00:00 2001 From: lquinoa252 Date: Tue, 21 Oct 2025 04:05:59 +0000 Subject: [PATCH 1/4] feat: implement GET /items/{id} endpoint - Add UUID parsing with error handling for malformed IDs - Search library using SearchCriteria with ID filter - Return 404 for non-existent items, 200 for found items - Return 400 for invalid UUID format --- .../lesson23/web/MediaItemsController.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java index 7efa0b2f8..2759cc51e 100644 --- a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java +++ b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java @@ -7,9 +7,11 @@ import java.io.IOException; import java.util.List; import java.util.Set; +import java.util.UUID; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController @@ -31,4 +33,22 @@ public ResponseEntity getItems() { var response = GetMediaItemsResponse.builder().items(responseItems).build(); return ResponseEntity.ok(response); } + + @GetMapping("/items/{id}") + public ResponseEntity getItem(@PathVariable String id) { + try { + UUID itemId = UUID.fromString(id); + Set items = library.search(SearchCriteria.builder().id(itemId.toString()).build()); + + if (items.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + MediaItem item = items.iterator().next(); + MediaItemResponse response = MediaItemResponse.from(item); + return ResponseEntity.ok(response); + } catch (IllegalArgumentException e) { + return ResponseEntity.badRequest().build(); + } + } } From 2a9190fc38ff4404dfe1c9594419295255ea8db1 Mon Sep 17 00:00:00 2001 From: lquinoa252 Date: Wed, 22 Oct 2025 15:06:51 +0000 Subject: [PATCH 2/4] feat: implement POST /items endpoint for creating media items - Add validation with @Valid for automatic error handling - Convert MediaItemRequest to MediaItem using existing helper - Add items to library via librarian authorization - Return created item in response with proper status codes --- .../lesson23/web/MediaItemsController.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java index 2759cc51e..9db57433c 100644 --- a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java +++ b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java @@ -8,10 +8,13 @@ import java.util.List; import java.util.Set; import java.util.UUID; +import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; 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.RestController; @RestController @@ -51,4 +54,21 @@ public ResponseEntity getItem(@PathVariable String id) { return ResponseEntity.badRequest().build(); } } + + @PostMapping("/items") + public ResponseEntity createItem(@Valid @RequestBody CreateMediaItemRequest request) { + try { + MediaItem newItem = MediaItemRequest.asMediaItem(request.getItem()); + library.addMediaItem(newItem, librarian); + + MediaItemResponse itemResponse = MediaItemResponse.from(newItem); + CreateMediaItemResponse response = CreateMediaItemResponse.builder() + .item(itemResponse) + .build(); + + return ResponseEntity.ok(response); + } catch (Exception e) { + return ResponseEntity.badRequest().build(); + } + } } From ff7ec8c9c98fc21de124592ffc6d45ae072e502f Mon Sep 17 00:00:00 2001 From: lquinoa252 Date: Wed, 22 Oct 2025 15:41:28 +0000 Subject: [PATCH 3/4] feat: implement DELETE /items/{id} endpoint - Remove all try/catch blocks - Add @RequestMapping class-level mapping - Let Spring handle validation and error responses automatically --- .../lesson23/web/MediaItemsController.java | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java index 9db57433c..44152a622 100644 --- a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java +++ b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java @@ -9,16 +9,20 @@ import java.util.Set; import java.util.UUID; import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.DeleteMapping; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @CrossOrigin +@RequestMapping public class MediaItemsController { private final Library library; @@ -30,45 +34,43 @@ public MediaItemsController(Library library) throws IOException { } @GetMapping("/items") - public ResponseEntity getItems() { + public GetMediaItemsResponse getItems() { Set items = library.search(SearchCriteria.builder().build()); List responseItems = items.stream().map(MediaItemResponse::from).toList(); - var response = GetMediaItemsResponse.builder().items(responseItems).build(); - return ResponseEntity.ok(response); + return GetMediaItemsResponse.builder().items(responseItems).build(); } @GetMapping("/items/{id}") public ResponseEntity getItem(@PathVariable String id) { - try { - UUID itemId = UUID.fromString(id); - Set items = library.search(SearchCriteria.builder().id(itemId.toString()).build()); - - if (items.isEmpty()) { - return ResponseEntity.notFound().build(); - } - - MediaItem item = items.iterator().next(); - MediaItemResponse response = MediaItemResponse.from(item); - return ResponseEntity.ok(response); - } catch (IllegalArgumentException e) { - return ResponseEntity.badRequest().build(); + UUID itemId = UUID.fromString(id); + Set items = library.search(SearchCriteria.builder().id(itemId.toString()).build()); + + if (items.isEmpty()) { + return ResponseEntity.notFound().build(); } + + MediaItem item = items.iterator().next(); + return ResponseEntity.ok(MediaItemResponse.from(item)); } @PostMapping("/items") - public ResponseEntity createItem(@Valid @RequestBody CreateMediaItemRequest request) { - try { - MediaItem newItem = MediaItemRequest.asMediaItem(request.getItem()); - library.addMediaItem(newItem, librarian); - - MediaItemResponse itemResponse = MediaItemResponse.from(newItem); - CreateMediaItemResponse response = CreateMediaItemResponse.builder() - .item(itemResponse) - .build(); - - return ResponseEntity.ok(response); - } catch (Exception e) { - return ResponseEntity.badRequest().build(); + public CreateMediaItemResponse createItem(@Valid @RequestBody CreateMediaItemRequest request) { + MediaItem newItem = MediaItemRequest.asMediaItem(request.getItem()); + library.addMediaItem(newItem, librarian); + + MediaItemResponse itemResponse = MediaItemResponse.from(newItem); + return CreateMediaItemResponse.builder().item(itemResponse).build(); + } + + @DeleteMapping("/items/{id}") + public ResponseEntity deleteItem(@PathVariable String id) { + UUID itemId = UUID.fromString(id); + + if (!library.hasMediaItem(itemId)) { + return ResponseEntity.notFound().build(); } + + library.removeMediaItem(itemId, librarian); + return ResponseEntity.noContent().build(); } } From 2a618ba651c21bcf7ea65c3f45626c7a80c6985c Mon Sep 17 00:00:00 2001 From: lquinoa252 Date: Wed, 22 Oct 2025 16:40:05 +0000 Subject: [PATCH 4/4] Fix code formatting with spotlessApply - Reorder imports (jakarta.validation.Valid before java.* imports) - Remove unused import (org.springframework.http.HttpStatus) - Clean up trailing whitespace on empty lines --- .../lesson23/web/MediaItemsController.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java index 44152a622..df79a1650 100644 --- a/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java +++ b/lesson_23/api/java/api_app/src/main/java/com/codedifferently/lesson23/web/MediaItemsController.java @@ -4,12 +4,11 @@ import com.codedifferently.lesson23.library.Library; import com.codedifferently.lesson23.library.MediaItem; import com.codedifferently.lesson23.library.search.SearchCriteria; +import jakarta.validation.Valid; import java.io.IOException; import java.util.List; import java.util.Set; import java.util.UUID; -import jakarta.validation.Valid; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; @@ -44,11 +43,11 @@ public GetMediaItemsResponse getItems() { public ResponseEntity getItem(@PathVariable String id) { UUID itemId = UUID.fromString(id); Set items = library.search(SearchCriteria.builder().id(itemId.toString()).build()); - + if (items.isEmpty()) { return ResponseEntity.notFound().build(); } - + MediaItem item = items.iterator().next(); return ResponseEntity.ok(MediaItemResponse.from(item)); } @@ -57,7 +56,7 @@ public ResponseEntity getItem(@PathVariable String id) { public CreateMediaItemResponse createItem(@Valid @RequestBody CreateMediaItemRequest request) { MediaItem newItem = MediaItemRequest.asMediaItem(request.getItem()); library.addMediaItem(newItem, librarian); - + MediaItemResponse itemResponse = MediaItemResponse.from(newItem); return CreateMediaItemResponse.builder().item(itemResponse).build(); } @@ -65,11 +64,11 @@ public CreateMediaItemResponse createItem(@Valid @RequestBody CreateMediaItemReq @DeleteMapping("/items/{id}") public ResponseEntity deleteItem(@PathVariable String id) { UUID itemId = UUID.fromString(id); - + if (!library.hasMediaItem(itemId)) { return ResponseEntity.notFound().build(); } - + library.removeMediaItem(itemId, librarian); return ResponseEntity.noContent().build(); }