Skip to content

Commit 5006fd3

Browse files
author
Mohamed
committed
Feat: Added patron controllers and tests
1 parent 78f1ab8 commit 5006fd3

File tree

8 files changed

+312
-5
lines changed

8 files changed

+312
-5
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import jakarta.validation.Valid;
4+
import jakarta.validation.constraints.NotNull;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
10+
@Data
11+
@AllArgsConstructor
12+
@NoArgsConstructor
13+
@Builder
14+
public class CreatePatronRequest {
15+
@NotNull(message = "patron is required") @Valid
16+
private PatronRequest patron;
17+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import lombok.Builder;
4+
import lombok.Data;
5+
6+
@Data
7+
@Builder
8+
public class CreatePatronResponse {
9+
private PatronResponse patron;
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import java.util.List;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.Singular;
7+
8+
@Data
9+
@Builder
10+
public class GetPatronsResponse {
11+
@Singular private List<PatronResponse> patrons;
12+
}

lesson_16/api/api_app/src/main/java/com/codedifferently/lesson16/web/MediaItemsController.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
import org.springframework.web.bind.annotation.RestController;
2020
import org.springframework.web.server.ResponseStatusException;
2121

22+
// ___________________________________________________________
23+
// THIS CODE WAS MADE IN COLLABORATION WITH VICENTE AND RICH
24+
// ___________________________________________________________
25+
2226
@RestController
2327
public class MediaItemsController {
2428
private final Library library;
@@ -45,8 +49,8 @@ public CreateMediaItemResponse postItem(@Valid @RequestBody CreateMediaItemReque
4549
}
4650

4751
@GetMapping("/items/{id}")
48-
public GetMediaItemsResponse getMediaItem(@PathVariable String id) {
49-
Set<MediaItem> items = library.search(SearchCriteria.builder().id(id).build());
52+
public GetMediaItemsResponse getItem(@PathVariable UUID id) {
53+
Set<MediaItem> items = library.search(SearchCriteria.builder().id(id.toString()).build());
5054
if (items.isEmpty()) {
5155
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Media item not found");
5256
}
@@ -56,11 +60,11 @@ public GetMediaItemsResponse getMediaItem(@PathVariable String id) {
5660
}
5761

5862
@DeleteMapping("/items/{id}")
59-
public ResponseEntity<Void> deleteMediaItem(@PathVariable String id) {
60-
if (!library.hasMediaItem(UUID.fromString(id))) {
63+
public ResponseEntity<Void> deleteItem(@PathVariable UUID id) {
64+
if (!library.hasMediaItem(id)) {
6165
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Media item not found");
6266
}
63-
library.removeMediaItem(UUID.fromString(id), librarian);
67+
library.removeMediaItem(id, librarian);
6468
return ResponseEntity.noContent().build();
6569
}
6670
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import com.codedifferently.lesson16.library.Patron;
4+
import jakarta.validation.constraints.NotBlank;
5+
import java.util.UUID;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Data;
9+
import lombok.NoArgsConstructor;
10+
11+
@Data
12+
@AllArgsConstructor
13+
@NoArgsConstructor
14+
@Builder
15+
public class PatronRequest {
16+
private UUID id;
17+
18+
@NotBlank(message = "Email is required")
19+
private String email;
20+
21+
@NotBlank(message = "Name is required")
22+
private String name;
23+
24+
public static Patron asPatron(PatronRequest request) {
25+
return new Patron(request.name, request.email);
26+
}
27+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import com.codedifferently.lesson16.library.LibraryGuest;
4+
import jakarta.validation.constraints.NotBlank;
5+
import java.util.UUID;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
9+
@Data
10+
@Builder
11+
public class PatronResponse {
12+
private UUID id;
13+
14+
@NotBlank(message = "Email is required")
15+
private String email;
16+
17+
@NotBlank(message = "Name is required")
18+
private String name;
19+
20+
public static PatronResponse from(LibraryGuest patron) {
21+
var result =
22+
PatronResponse.builder().id(patron.getId()).name(patron.getName()).email(patron.getEmail());
23+
return result.build();
24+
}
25+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import com.codedifferently.lesson16.library.Library;
4+
import com.codedifferently.lesson16.library.LibraryGuest;
5+
import com.codedifferently.lesson16.library.Patron;
6+
import jakarta.validation.Valid;
7+
import java.io.IOException;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Set;
11+
import java.util.UUID;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.web.bind.annotation.DeleteMapping;
15+
import org.springframework.web.bind.annotation.GetMapping;
16+
import org.springframework.web.bind.annotation.PathVariable;
17+
import org.springframework.web.bind.annotation.PostMapping;
18+
import org.springframework.web.bind.annotation.RequestBody;
19+
import org.springframework.web.bind.annotation.RestController;
20+
import org.springframework.web.server.ResponseStatusException;
21+
22+
@RestController
23+
public class PatronsController {
24+
private final Library library;
25+
26+
public PatronsController(Library library) throws IOException {
27+
this.library = library;
28+
}
29+
30+
@GetMapping("/patrons")
31+
public GetPatronsResponse getPatrons() {
32+
Set<LibraryGuest> patrons = library.getPatrons();
33+
List<PatronResponse> responsePatrons = patrons.stream().map(PatronResponse::from).toList();
34+
var response = GetPatronsResponse.builder().patrons(responsePatrons).build();
35+
return response;
36+
}
37+
38+
@PostMapping("/patrons")
39+
public CreatePatronResponse postPatron(@Valid @RequestBody CreatePatronRequest req) {
40+
Patron guest = PatronRequest.asPatron(req.getPatron());
41+
library.addLibraryGuest(guest);
42+
return CreatePatronResponse.builder().patron(PatronResponse.from(guest)).build();
43+
}
44+
45+
@GetMapping("/patrons/{id}")
46+
public GetPatronsResponse getPatron(@PathVariable("id") UUID id) {
47+
if (!library.hasLibraryGuest(id)) {
48+
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Guest patron not found");
49+
}
50+
Set<LibraryGuest> patrons = new HashSet<>();
51+
for (LibraryGuest guest : library.getPatrons()) {
52+
if (guest.getId() == id) {
53+
patrons.add(guest);
54+
}
55+
}
56+
List<PatronResponse> responsePatrons = patrons.stream().map(PatronResponse::from).toList();
57+
var response = GetPatronsResponse.builder().patrons(responsePatrons).build();
58+
return response;
59+
}
60+
61+
@DeleteMapping("/patrons/{id}")
62+
public ResponseEntity<Void> deletePatron(@PathVariable() UUID id) {
63+
if (!library.hasLibraryGuest(id)) {
64+
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Guest patron not found");
65+
}
66+
library.removeLibraryGuest(id);
67+
return ResponseEntity.noContent().build();
68+
}
69+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.codedifferently.lesson16.web;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
5+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
6+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
7+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
8+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
9+
10+
import com.codedifferently.lesson16.Lesson16;
11+
import com.codedifferently.lesson16.library.Library;
12+
import com.codedifferently.lesson16.library.LibraryGuest;
13+
import com.codedifferently.lesson16.library.Patron;
14+
import java.util.HashSet;
15+
import java.util.Set;
16+
import java.util.UUID;
17+
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Test;
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.boot.test.context.SpringBootTest;
21+
import org.springframework.http.MediaType;
22+
import org.springframework.test.context.ContextConfiguration;
23+
import org.springframework.test.web.servlet.MockMvc;
24+
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
25+
import org.springframework.web.context.WebApplicationContext;
26+
27+
@SpringBootTest
28+
@ContextConfiguration(classes = Lesson16.class)
29+
class PatronControllerTest {
30+
private static MockMvc mockMvc;
31+
@Autowired private Library library;
32+
33+
@BeforeAll
34+
static void setUp(WebApplicationContext wac) {
35+
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
36+
}
37+
38+
@Test
39+
void testController_getsAllPatrons() throws Exception {
40+
mockMvc
41+
.perform(get("/patrons").contentType(MediaType.APPLICATION_JSON))
42+
.andExpect(status().isOk())
43+
.andExpect(jsonPath("$.patrons").isArray())
44+
.andExpect(jsonPath("$.patrons.length()").value(5));
45+
}
46+
47+
@Test
48+
void testController_deletesPatron() throws Exception {
49+
Set<LibraryGuest> pat = library.getPatrons();
50+
UUID ids = UUID.fromString("00000000-0000-0000-0000-000000000000");
51+
for (LibraryGuest guest : pat) {
52+
if (guest.getName() == "Alice Johnson") {
53+
ids = guest.getId();
54+
}
55+
}
56+
57+
mockMvc
58+
.perform(delete("/patrons/" + ids.toString()).contentType(MediaType.APPLICATION_JSON))
59+
.andExpect(status().isNoContent());
60+
int i = 0;
61+
for (LibraryGuest guest : pat) {
62+
if (guest.getName() == "Alice Johnson") {
63+
i++;
64+
}
65+
}
66+
assertThat(i).isEqualTo(0);
67+
}
68+
69+
@Test
70+
void testController_getsAnPatron() throws Exception {
71+
Set<LibraryGuest> pat = library.getPatrons();
72+
UUID ids = UUID.fromString("00000000-0000-0000-0000-000000000000");
73+
for (LibraryGuest guest : pat) {
74+
if (guest.getName() == "Bob Williams") {
75+
ids = guest.getId();
76+
}
77+
}
78+
mockMvc
79+
.perform(get("/patrons/" + ids.toString()).contentType(MediaType.APPLICATION_JSON))
80+
.andExpect(status().isOk());
81+
}
82+
83+
@Test
84+
void testController_returnsNotFoundOnGetPatron() throws Exception {
85+
mockMvc
86+
.perform(
87+
get("/patrons/00000000-0000-0000-0000-000000000000")
88+
.contentType(MediaType.APPLICATION_JSON))
89+
.andExpect(status().isNotFound());
90+
}
91+
92+
@Test
93+
void testController_reportsBadRequestOnAddPatron() throws Exception {
94+
String json = "{}";
95+
96+
mockMvc
97+
.perform(post("/patrons").contentType(MediaType.APPLICATION_JSON).content(json))
98+
.andExpect(status().isBadRequest())
99+
.andExpect(jsonPath("$.errors").isArray())
100+
.andExpect(jsonPath("$.errors.length()").value(1));
101+
}
102+
103+
@Test
104+
void testController_addsPatron() throws Exception {
105+
String json =
106+
"""
107+
{
108+
"guests":{
109+
{
110+
"id": "ca4a7abd-95fa-43db-91c7-5d80e27d821a"
111+
"name": "John Book",
112+
"email": "john.Book@reallibrary.org",
113+
"checkedOutPatrons": []
114+
}
115+
}
116+
""";
117+
118+
mockMvc
119+
.perform(post("/patrons").contentType(MediaType.APPLICATION_JSON).content(json))
120+
.andExpect(status().isOk())
121+
.andExpect(jsonPath("$.patron.id").value("ca4a7abd-95fa-43db-91c7-5d80e27d821a"));
122+
123+
Set<LibraryGuest> patrons = new HashSet<>();
124+
for (LibraryGuest guest : library.getInfo().getGuests()) {
125+
if (guest.getId() == UUID.fromString("ca4a7abd-95fa-43db-91c7-5d80e27d821a")) {
126+
patrons.add(guest);
127+
}
128+
}
129+
assertThat(patrons).hasSize(1);
130+
var patron = patrons.iterator().next();
131+
assertThat(patron).isInstanceOf(Patron.class);
132+
assertThat(patron.getName()).isEqualTo("John Book");
133+
}
134+
135+
@Test
136+
void testController_returnsNotFoundOnDeletePatron() throws Exception {
137+
mockMvc
138+
.perform(
139+
delete("/patrons/00000000-0000-0000-0000-000000000000")
140+
.contentType(MediaType.APPLICATION_JSON))
141+
.andExpect(status().isNotFound());
142+
}
143+
}

0 commit comments

Comments
 (0)