Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement MediaItemsController and PatronsController Added endpoints for managing media items and patrons #365

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7457df4
feat: implemented librarian, Magazine,Newspaper,Exceptions
Nelshulla Mar 27, 2024
b5d1c05
feat: Added localhost for readme
Nelshulla Apr 3, 2024
f5e0c9f
Revert "feat: implemented librarian, Magazine,Newspaper,Exceptions"
Nelshulla Apr 3, 2024
73311c9
feat: Changed/deleted files
Nelshulla Apr 3, 2024
0bc2cc5
Delete package-lock.json
Nelshulla Apr 3, 2024
c2194db
Delete package.json
Nelshulla Apr 3, 2024
122c33a
Delete /ts-node-express/tsconfig.json
Nelshulla Apr 3, 2024
7474ee7
feat: added package.json
Nelshulla Apr 3, 2024
7c936e5
feat: Implement MediaItemsController and PatronsController
Nelshulla Apr 8, 2024
9e03d7a
Revert "feat: Added localhost for readme"
Nelshulla Apr 8, 2024
b262523
Revert "feat: Changed/deleted files"
Nelshulla Apr 8, 2024
bd5c504
Revert "Delete /ts-node-express/tsconfig.json"
Nelshulla Apr 8, 2024
f7eb0db
Revert "Delete package.json"
Nelshulla Apr 8, 2024
0006a2e
Revert "Delete package-lock.json"
Nelshulla Apr 8, 2024
2f7fc24
Reapply "feat: implemented librarian, Magazine,Newspaper,Exceptions"
Nelshulla Apr 8, 2024
f0bf887
Revert "Reapply "feat: implemented librarian, Magazine,Newspaper,Exce…
Nelshulla Apr 8, 2024
c19baa9
Revert "feat: added package.json"
Nelshulla Apr 8, 2024
0a5be93
fix: Correct failing test cases for PatronsController
Nelshulla Apr 9, 2024
b58e788
Delete lesson_15/ts-node-express/package-lock.json
Nelshulla Apr 9, 2024
871a444
Delete lesson_15/ts-node-express/package.json
Nelshulla Apr 9, 2024
edee047
Delete lesson_15/ts-node-express/public/images
Nelshulla Apr 9, 2024
b0749d7
Delete lesson_15/ts-node-express/public/index.html
Nelshulla Apr 9, 2024
8052798
Delete lesson_15/ts-node-express/src/index.js
Nelshulla Apr 9, 2024
e43d71f
Delete lesson_15/ts-node-express/src/index.ts
Nelshulla Apr 9, 2024
a3c4da1
Delete lesson_15/ts-node-express/tsconfig.json
Nelshulla Apr 9, 2024
0df852b
Delete package-lock.json
Nelshulla Apr 9, 2024
6813e42
Delete package.json
Nelshulla Apr 9, 2024
a346f13
feat: Syntax error and ran spotlessApply
Nelshulla Apr 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@
import com.codedifferently.lesson16.library.Librarian;
import com.codedifferently.lesson16.library.Library;
import com.codedifferently.lesson16.library.MediaItem;
import com.codedifferently.lesson16.library.exceptions.MediaItemCheckedOutException;
import com.codedifferently.lesson16.library.search.SearchCriteria;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.UUID;
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.RestController;
import org.springframework.web.server.ResponseStatusException;

/**
* @author vscode
*/
@RestController
@CrossOrigin
public class MediaItemsController {
Expand All @@ -29,4 +42,37 @@ public GetMediaItemsResponse getItems() {
var response = GetMediaItemsResponse.builder().items(responseItems).build();
return response;
}

@PostMapping("/items")
public CreateMediaItemResponse postItem(@Valid @RequestBody CreateMediaItemRequest request) {
MediaItem item = MediaItemRequest.asMediaItem(request.getItem());
library.addMediaItem(item, librarian);
var response = CreateMediaItemResponse.builder().item(MediaItemResponse.from(item)).build();
return response;
}

@GetMapping("/items/{id}")
public GetMediaItemsResponse getMediaItem(@PathVariable String id) {
Set<MediaItem> items = library.search(SearchCriteria.builder().id(id).build());
if (items.isEmpty()) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Media item not found");
}
List<MediaItemResponse> responseItems = items.stream().map(MediaItemResponse::from).toList();
var response = GetMediaItemsResponse.builder().items(responseItems).build();
return response;
}

@DeleteMapping("/items/{id}")
public ResponseEntity<Void> deleteItem(@PathVariable("id") UUID id) {
if (!library.hasMediaItem(id)) {
return ResponseEntity.notFound().build();
}
try {
library.removeMediaItem(id, new Librarian("Default", "librarian@example.com"));
return ResponseEntity.noContent().build();
} catch (MediaItemCheckedOutException e) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST, "Cannot delete checked out item", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.codedifferently.lesson16.web;

import com.codedifferently.lesson16.library.Library;
import com.codedifferently.lesson16.library.LibraryGuest;
import com.codedifferently.lesson16.library.exceptions.MediaItemCheckedOutException;
import jakarta.validation.Valid;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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
@RequestMapping("/patrons")
public class PatronsController {

private final Library library;
private final Map<UUID, LibraryGuest> patronMap;

public PatronsController(Library library) {
this.library = library;
this.patronMap = new HashMap<>();
}

@GetMapping
public ResponseEntity<Collection<LibraryGuest>> getPatrons() {
Collection<LibraryGuest> patrons = library.getPatrons();
return ResponseEntity.ok(patrons);
}

@PostMapping
public ResponseEntity<Void> createPatron(@Valid @RequestBody PatronsRequest request) {
LibraryGuest guest = PatronsRequest.asLibraryGuest(request);
library.addLibraryGuest(guest);
return ResponseEntity.status(HttpStatus.CREATED).build();
}

@GetMapping("/{id}")
public ResponseEntity<LibraryGuest> getPatronById(@PathVariable UUID id) {
LibraryGuest patron = patronMap.get(id);
if (patron != null) {
return ResponseEntity.ok(patron);
} else {
return ResponseEntity.notFound().build();
}
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> removePatron(@PathVariable UUID id) {
try {
library.removeLibraryGuest(id);
return ResponseEntity.noContent().build();
} catch (MediaItemCheckedOutException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
} catch (Exception e) {
return ResponseEntity.notFound().build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.codedifferently.lesson16.web;

import com.codedifferently.lesson16.library.LibraryGuest;
import com.codedifferently.lesson16.library.Patron;
import jakarta.validation.constraints.NotBlank;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PatronsRequest {
private UUID id;

@NotBlank(message = "Name is required")
private String name;

@NotBlank(message = "Email is required")
private String email;

public static LibraryGuest asLibraryGuest(PatronsRequest request) {
UUID id = request.getId();
String name = request.getName();
String email = request.getEmail();

return new Patron(name, email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.codedifferently.lesson16.web;

import com.codedifferently.lesson16.library.LibraryGuest;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class PatronsResponse {
private UUID id;
private String name;

public static PatronsResponse from(LibraryGuest guest) {
return builder().id(guest.getId()).name(guest.getName()).build();
}

public static List<PatronsResponse> from(Collection<LibraryGuest> guests) {
return guests.stream().map(PatronsResponse::from).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.codedifferently.lesson16.web;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
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.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.codedifferently.lesson16.library.Library;
import com.codedifferently.lesson16.library.LibraryGuest;
import java.util.Collections;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

class PatronsControllerTest {

private MockMvc mockMvc;
private Library library;

@BeforeEach
public void setUp() {
library = mock(Library.class);
mockMvc = MockMvcBuilders.standaloneSetup(new PatronsController(library)).build();
}

@Test
public void testGetPatrons() throws Exception {
// Mocking
Set<LibraryGuest> patrons = Collections.emptySet();
when(library.getPatrons()).thenReturn(patrons);

// Execution and Assertion
mockMvc
.perform(get("/patrons"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}

@Test
public void testCreatePatron() throws Exception {
// Mocking
mockMvc
.perform(
post("/patrons")
.contentType(MediaType.APPLICATION_JSON)
.content(
"{\"id\": \"123e4567-e89b-12d3-a456-426614174000\", \"name\": \"John Doe\", \"email\": \"john@example.com\"}")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated());
}

@Test
public void testRemovePatron() throws Exception {
// Execution and Assertion
mockMvc
.perform(delete("/patrons/{id}", "123e4567-e89b-12d3-a456-426614174000"))
.andExpect(status().isNoContent());
}
}