The project aims to create a comprehensive library management system that facilitates efficient management of users, books, loans, fines, and reservations.
- User Registration and Management:
- The system allows adding, modifying, and displaying users.
- Book Management:
- The system allows adding, modifying, and displaying books.
- Loan Management:
- The system allows adding, modifying, and displaying loans.
- Reservation Management:
- The system allows adding, modifying, and displaying reservations.
- Fine Management:
- The system allows adding, modifying, and displaying fines.
- Deletion:
- The system allows users to be deleted.
- The system allows books to be deleted.
- The system allows loans, reservations, and fines to be deleted.
- Fines Displaying:
- The system allows the display of a user's fines.
- Loans Displaying:
- The system allows the display of a user's loans.
- Loans Reservations:
- The system allows the display of a user's reservations.
- Database Storage:
- The system uses a database to store information about users, books, fines, loans, and reservations.
The system should allow administrators to manage user information, including registration, retrieval, update, and deletion of user profiles.
- User registration with essential details (firstName, lastName, email).
- View and edit existing user profiles.
- Delete user accounts if needed.
Description: The system should enable efficient handling of the library's book inventory, covering aspects like adding new books, updating book details, and removing books from circulation.
- Add new books to the library inventory with details such as title, author, and ISBN.
- Update existing book information.
Description: The system should support the process of loaning books to users and managing the return of borrowed items, including tracking due dates and fines.
- Loan books to users, associating them with due dates.
- Record book returns and calculate fines for overdue items.
- Display a user's active loans.
Description: The system should allow users to reserve books that are currently unavailable, providing a queue system for popular titles.
- Users can place reservations for books that are currently on loan or unavailable.
- Display a user's reservations.
Description: The system should handle fines accrued by users for overdue book returns, allowing administrators to manage and users to view their fines.
- Automatically calculate fines based on due dates for returned books.
- Display a user's current fines.
- Admins can view and manage fines, including marking them as paid.
- Create a User
- Create a Book
- Create a Reservation
- Create a Loan
- Create a Fine
- Create an Address
- Get all Users
- Get User by ID
- Get the Reservation of the user with the specific ID
- Get Loans of the user with the specific ID
- Get Fines of the user with the specific ID
- Get all Books
- Get Book by ID
- Get all Reservations
- Get a Reservation by ID
- Get all Loans
- Get a Loan by ID
- Get all Fines
- Get Fine by ID
- Get all Addresses
- Get the Address by ID
- Update User details
- Update Book details
- Update Reservation details
- Update Loan details
- Update Fine details
- Update Address details
- Deleting a User and related Address
- Cancellation of a Reservation
- Delete a Loan
- Delete a Fine
- Delete a Book
- Delete an Address
- UserController /users
- BookController /books
- LoanController /loans
- FineController /fines
- ReservationController /reservations
- AddressController /addresses
@RestController
@RequestMapping("/books")
public class BookController {
private final BookService bookService;
@Autowired
public BookController(BookService bookService) {
this.bookService = bookService;
}
@PostMapping
@Operation(summary = "Create a book",
description = "Returns the new book")
public ResponseEntity<BookDto> createBook(@RequestBody @Valid BookDto bookDto) {
...
}
@PutMapping("/{id}")
@Operation(summary = "Update a book",
description = "Update a book's information based on the provided book ID")
public ResponseEntity<BookDto> updateBook(@PathVariable Long id,
@RequestBody @Valid BookDto bookDto) {
BookDto updatedBook = bookService.updateBook(id, bookDto);
...
}
@DeleteMapping("/{id}")
@Operation(summary = "Delete a book",
description = "Delete a book by id from the database")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
...
}
...
}
- UserService
- BookService
- LoanService
- FineService
- ReservationService
- AddressService
@Service
public class BookService {
private final BookRepository bookRepository;
private final BookMapper bookMapper;
@Autowired
public BookService(BookRepository bookRepository,
BookMapper bookMapper) {
this.bookRepository = bookRepository;
this.bookMapper = bookMapper;
}
public BookDto createBook(BookDto bookDto) {
...
}
public BookDto updateBook(Long id,
BookDto bookDto) {
...
}
public boolean deleteBook(Long id) {
...
}
...
}
- Each entity has an associated interface that extends JpaRepository<Entity, Type_ID> where the methods that were not in the basic interface were defined (those already existing: findById, findAllById, delete, save, ...)
@Repository
public interface UserRepository
extends JpaRepository<User, Long> {
List<User> findAllByAddress_Street(String street);
@Query( nativeQuery = true, value = "select * from user where firstName = :name")
User findUserByFirstNameWithNativeQuery(String name);
Optional<User> findByEmail(String email);
}
- The classes of type @Component with the purpose of mapping request-type entities into entities used as a model in the database
@Component
public class UserMapper {
private final AddressMapper addressMapper;
public UserMapper(AddressMapper addressMapper) {
this.addressMapper = addressMapper;
}
public List<UserDto> mapListToStudentDto(List<User> users){
return users.stream().map(this::mapToUserDto).collect(Collectors.toList());
}
public User mapToUser(UserDto userDto) {
...
}
public UserDto mapToUserDto(User user) {
...
}
}
- Each entity of type EntityNameDto contains all field validations according to type
public class UserDto {
@Schema(accessMode = READ_ONLY)
private Long id;
@NotNull(message = "First name is mandatory.")
@NotBlank(message = "First Name must have a value.")
@Schema(name = "firstName", example = "John")
private String firstName;
@NotNull(message = "Last name is mandatory.")
@NotBlank(message = "Last Name must have a value.")
@Schema(name = "lastName", example = "Smith")
private String lastName;
@NotNull(message = "Email is mandatory.")
@Email(message = "Email should be valid.")
@Schema(name = "email", example = "email@example.com")
private String email;
...
}
UNIT tests for REST endpoints and services with all tests passed.
public class BookControllerUnitTest {
private MockMvc mockMvc;
@Mock
private BookService service;
@InjectMocks
private BookController controller;
...
@Test
public void testGetAllBooks() throws Exception {
List<BookDto> dtoList = getDummyDtos();
Mockito.when(service.getAllBooks()).thenReturn(dtoList);
mockMvc.perform(MockMvcRequestBuilders
.get("/books"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().json(new ObjectMapper().writeValueAsString(dtoList)));
}
...
}
public class BookServiceUnitTest {
@Mock
private BookRepository repository;
@InjectMocks
private BookService service;
@BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void testDeleteAddress_SuccessfulDeletion() {
Long idToDelete = 1L;
when(repository.existsById(idToDelete)).thenReturn(true);
boolean result = service.deleteBook(idToDelete);
assertTrue(result); // Deletion should be successful
verify(repository, times(1)).deleteById(idToDelete);
}
...
}
http://localhost:8081/swagger-ui/index.html
Contains information for each API
@GetMapping("/{id}")
@Operation(summary = "Get a User by id", description = "Returns a user as per the id")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved"),
@ApiResponse(responseCode = "404", description = "Not found - The User was not found")
})
public ResponseEntity<UserDto> getById(@PathVariable @Parameter(example = "1") Long id) {
return ResponseEntity.ok().body(userService.getById(id));
}