diff --git a/src/main/java/com/libraryman_api/LibrarymanApiApplication.java b/src/main/java/com/libraryman_api/LibrarymanApiApplication.java index fcd4665..89e3ed7 100644 --- a/src/main/java/com/libraryman_api/LibrarymanApiApplication.java +++ b/src/main/java/com/libraryman_api/LibrarymanApiApplication.java @@ -12,8 +12,8 @@ @EnableCaching public class LibrarymanApiApplication { - public static void main(String[] args) { - SpringApplication.run(LibrarymanApiApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(LibrarymanApiApplication.class, args); + } } \ No newline at end of file diff --git a/src/main/java/com/libraryman_api/book/Book.java b/src/main/java/com/libraryman_api/book/Book.java index d3cdb31..f037f2d 100644 --- a/src/main/java/com/libraryman_api/book/Book.java +++ b/src/main/java/com/libraryman_api/book/Book.java @@ -50,60 +50,62 @@ public int getBookId() { return bookId; } - public String getTitle() { - return title; - } - - public String getAuthor() { - return author; - } - - public String getIsbn() { - return isbn; - } - - public String getPublisher() { - return publisher; + public void setBookId(int bookId) { + this.bookId = bookId; } - public int getPublishedYear() { - return publishedYear; - } - - public String getGenre() { - return genre; - } - - public int getCopiesAvailable() { - return copiesAvailable; + public String getTitle() { + return title; } - public void setBookId(int bookId) {this.bookId = bookId;} - public void setTitle(String title) { this.title = title; } + public String getAuthor() { + return author; + } + public void setAuthor(String author) { this.author = author; } + public String getIsbn() { + return isbn; + } + public void setIsbn(String isbn) { this.isbn = isbn; } + public String getPublisher() { + return publisher; + } + public void setPublisher(String publisher) { this.publisher = publisher; } + public int getPublishedYear() { + return publishedYear; + } + public void setPublishedYear(int publishedYear) { this.publishedYear = publishedYear; } + public String getGenre() { + return genre; + } + public void setGenre(String genre) { this.genre = genre; } + public int getCopiesAvailable() { + return copiesAvailable; + } + public void setCopiesAvailable(int copiesAvailable) { this.copiesAvailable = copiesAvailable; } diff --git a/src/main/java/com/libraryman_api/book/BookController.java b/src/main/java/com/libraryman_api/book/BookController.java index 8a59125..3cd3af3 100644 --- a/src/main/java/com/libraryman_api/book/BookController.java +++ b/src/main/java/com/libraryman_api/book/BookController.java @@ -28,26 +28,26 @@ public class BookController { * Retrieves a paginated and sorted list of all books in the library. * * @param pageable contains pagination information (page number, size, and sorting). - * @param sortBy (optional) the field by which to sort the results. - * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. + * @param sortBy (optional) the field by which to sort the results. + * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. * @return a {@link Page} of {@link BookDto} objects representing the books in the library. - * The results are sorted by title by default and limited to 5 books per page. + * The results are sorted by title by default and limited to 5 books per page. */ @GetMapping - public Page getAllBooks(@PageableDefault(page=0, size=5, sort="title") Pageable pageable, - @RequestParam(required = false) String sortBy, - @RequestParam(required = false) String sortDir) { - + public Page getAllBooks(@PageableDefault(page = 0, size = 5, sort = "title") Pageable pageable, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir) { + // Adjust the pageable based on dynamic sorting parameters - if(sortBy!=null && !sortBy.isEmpty()) { - Sort.Direction direction= Sort.Direction.ASC; // Default direction - - if(sortDir!=null && sortDir.equalsIgnoreCase("desc")) { - direction = Sort.Direction.DESC; - } - - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)) ; - } + if (sortBy != null && !sortBy.isEmpty()) { + Sort.Direction direction = Sort.Direction.ASC; // Default direction + + if (sortDir != null && sortDir.equalsIgnoreCase("desc")) { + direction = Sort.Direction.DESC; + } + + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)); + } return bookService.getAllBooks(pageable); } @@ -80,7 +80,7 @@ public BookDto addBook(@RequestBody BookDto bookDto) { /** * Updates an existing book in the library. * - * @param id the ID of the book to update. + * @param id the ID of the book to update. * @param bookDtoDetails the {@link Book} object containing the updated book details. * @return the updated {@link Book} object. */ diff --git a/src/main/java/com/libraryman_api/book/BookDto.java b/src/main/java/com/libraryman_api/book/BookDto.java index b6a5d0e..e6e480d 100644 --- a/src/main/java/com/libraryman_api/book/BookDto.java +++ b/src/main/java/com/libraryman_api/book/BookDto.java @@ -5,17 +5,11 @@ public class BookDto { private int bookId; private String title; - private String author; - private String isbn; - private String publisher; - - private int publishedYear; private String genre; - private int copiesAvailable; public BookDto(int bookId, String title, String author, String isbn, String publisher, int publishedYear, String genre, int copiesAvailable) { diff --git a/src/main/java/com/libraryman_api/book/BookService.java b/src/main/java/com/libraryman_api/book/BookService.java index fc919d1..292684c 100644 --- a/src/main/java/com/libraryman_api/book/BookService.java +++ b/src/main/java/com/libraryman_api/book/BookService.java @@ -1,7 +1,7 @@ package com.libraryman_api.book; -import java.util.Optional; - +import com.libraryman_api.exception.InvalidSortFieldException; +import com.libraryman_api.exception.ResourceNotFoundException; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Caching; @@ -10,8 +10,7 @@ import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.stereotype.Service; -import com.libraryman_api.exception.InvalidSortFieldException; -import com.libraryman_api.exception.ResourceNotFoundException; +import java.util.Optional; /** * Service class for managing books in the LibraryMan system. @@ -52,7 +51,7 @@ public BookService(BookRepository bookRepository) { @Cacheable(value = "books") public Page getAllBooks(Pageable pageable) { - try { + try { Page pagedBooks = bookRepository.findAll(pageable); return pagedBooks.map(this::EntityToDto); } catch (PropertyReferenceException ex) { @@ -67,7 +66,7 @@ public Page getAllBooks(Pageable pageable) { * @return an {@code Optional} containing the found book, or {@code Optional.empty()} if no book was found */ - @Cacheable(value = "books", key ="#bookId") + @Cacheable(value = "books", key = "#bookId") public Optional getBookById(int bookId) { Optional bookById = bookRepository.findById(bookId); @@ -91,7 +90,7 @@ public BookDto addBook(BookDto bookDto) { /** * Updates an existing book with the given details. * - * @param bookId the ID of the book to update + * @param bookId the ID of the book to update * @param bookDtoDetails the new details for the book * @return the updated book * @throws ResourceNotFoundException if the book with the specified ID is not found @@ -131,6 +130,7 @@ public void deleteBook(int bookId) { .orElseThrow(() -> new ResourceNotFoundException("Book not found")); bookRepository.delete(book); } + /** * Converts a Book entity to a BookDto object. * @@ -143,8 +143,8 @@ public void deleteBook(int bookId) { * @return a BookDto object with data populated from the entity */ - public BookDto EntityToDto(Book book){ - BookDto bookDto= new BookDto(); + public BookDto EntityToDto(Book book) { + BookDto bookDto = new BookDto(); bookDto.setBookId(book.getBookId()); bookDto.setPublisher(book.getPublisher()); bookDto.setPublishedYear(book.getPublishedYear()); @@ -155,6 +155,7 @@ public BookDto EntityToDto(Book book){ bookDto.setCopiesAvailable(book.getCopiesAvailable()); return bookDto; } + /** * Converts a BookDto object to a Book entity. * @@ -168,16 +169,16 @@ public BookDto EntityToDto(Book book){ */ - public Book DtoToEntity(BookDto bookDto){ - Book book= new Book(); - book.setBookId(bookDto.getBookId()); - book.setAuthor(bookDto.getAuthor()); - book.setGenre(bookDto.getGenre()); - book.setPublisher(bookDto.getPublisher()); - book.setPublishedYear(bookDto.getPublishedYear()); - book.setTitle(bookDto.getTitle()); - book.setCopiesAvailable(bookDto.getCopiesAvailable()); - book.setIsbn(bookDto.getIsbn()); - return book; + public Book DtoToEntity(BookDto bookDto) { + Book book = new Book(); + book.setBookId(bookDto.getBookId()); + book.setAuthor(bookDto.getAuthor()); + book.setGenre(bookDto.getGenre()); + book.setPublisher(bookDto.getPublisher()); + book.setPublishedYear(bookDto.getPublishedYear()); + book.setTitle(bookDto.getTitle()); + book.setCopiesAvailable(bookDto.getCopiesAvailable()); + book.setIsbn(bookDto.getIsbn()); + return book; } } diff --git a/src/main/java/com/libraryman_api/borrowing/BorrowingController.java b/src/main/java/com/libraryman_api/borrowing/BorrowingController.java index 2c36af8..2b69a68 100644 --- a/src/main/java/com/libraryman_api/borrowing/BorrowingController.java +++ b/src/main/java/com/libraryman_api/borrowing/BorrowingController.java @@ -1,14 +1,12 @@ package com.libraryman_api.borrowing; import com.libraryman_api.exception.ResourceNotFoundException; - -import org.springframework.security.access.prepost.PreAuthorize; - import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; /** @@ -35,27 +33,27 @@ public BorrowingController(BorrowingService borrowingService) { * Retrieves a paginated and sorted list of all borrowing records in the library. * * @param pageable contains pagination information (page number, size, and sorting). - * @param sortBy (optional) the field by which to sort the results. - * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. + * @param sortBy (optional) the field by which to sort the results. + * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. * @return a {@link Page} of {@link Borrowings} representing all borrowings. - * The results are sorted by borrow date by default and limited to 5 members per page. + * The results are sorted by borrow date by default and limited to 5 members per page. */ @GetMapping @PreAuthorize("hasRole('LIBRARIAN') or hasRole('ADMIN')") - public Page getAllBorrowings(@PageableDefault(page=0, size=5, sort="borrowDate") Pageable pageable, - @RequestParam(required = false) String sortBy, - @RequestParam(required = false) String sortDir) { - - // Adjust the pageable based on dynamic sorting parameters - if(sortBy!=null && !sortBy.isEmpty()) { - Sort.Direction direction= Sort.Direction.ASC; // Default direction + public Page getAllBorrowings(@PageableDefault(page = 0, size = 5, sort = "borrowDate") Pageable pageable, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir) { - if(sortDir!=null && sortDir.equalsIgnoreCase("desc")) { - direction = Sort.Direction.DESC; - } + // Adjust the pageable based on dynamic sorting parameters + if (sortBy != null && !sortBy.isEmpty()) { + Sort.Direction direction = Sort.Direction.ASC; // Default direction - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)) ; - } + if (sortDir != null && sortDir.equalsIgnoreCase("desc")) { + direction = Sort.Direction.DESC; + } + + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)); + } return borrowingService.getAllBorrowings(pageable); } @@ -99,28 +97,28 @@ public String payFine(@PathVariable int id) { * * @param memberId the ID of the member whose borrowing records are to be retrieved. * @param pageable contains pagination information (page number, size, and sorting). - * @param sortBy (optional) the field by which to sort the results. - * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. + * @param sortBy (optional) the field by which to sort the results. + * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. * @return a {@link Page} of {@link Borrowings} representing all borrowings for a specific member. - * The results are sorted by borrow date by default and limited to 5 members per page. + * The results are sorted by borrow date by default and limited to 5 members per page. */ @GetMapping("member/{memberId}") @PreAuthorize("hasRole('LIBRARIAN') or hasRole('ADMIN') or (hasRole('USER') and #memberId == authentication.principal.memberId)") public Page getAllBorrowingsOfAMember(@PathVariable int memberId, - @PageableDefault(page=0, size=5, sort="borrowDate") Pageable pageable, - @RequestParam(required = false) String sortBy, - @RequestParam(required = false) String sortDir) { - - // Adjust the pageable based on dynamic sorting parameters - if(sortBy!=null && !sortBy.isEmpty()) { - Sort.Direction direction= Sort.Direction.ASC; // Default direction - - if(sortDir!=null && sortDir.equalsIgnoreCase("desc")) { - direction = Sort.Direction.DESC; - } - - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)) ; - } + @PageableDefault(page = 0, size = 5, sort = "borrowDate") Pageable pageable, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir) { + + // Adjust the pageable based on dynamic sorting parameters + if (sortBy != null && !sortBy.isEmpty()) { + Sort.Direction direction = Sort.Direction.ASC; // Default direction + + if (sortDir != null && sortDir.equalsIgnoreCase("desc")) { + direction = Sort.Direction.DESC; + } + + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)); + } return borrowingService.getAllBorrowingsOfMember(memberId, pageable); } diff --git a/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java b/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java index 4c1fe27..e718aaf 100644 --- a/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java +++ b/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java @@ -1,17 +1,18 @@ package com.libraryman_api.borrowing; -import java.util.List; - import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface BorrowingRepository extends JpaRepository { // Underscore (_) used for property traversal, navigating from Borrowings to Members entity via 'member' property Page findByMember_memberId(int memberId, Pageable pageable); + List findByBook_bookId(int bookId); } diff --git a/src/main/java/com/libraryman_api/borrowing/BorrowingService.java b/src/main/java/com/libraryman_api/borrowing/BorrowingService.java index 7a1c510..6975e71 100644 --- a/src/main/java/com/libraryman_api/borrowing/BorrowingService.java +++ b/src/main/java/com/libraryman_api/borrowing/BorrowingService.java @@ -1,22 +1,22 @@ package com.libraryman_api.borrowing; +import com.libraryman_api.book.Book; import com.libraryman_api.book.BookDto; import com.libraryman_api.book.BookService; -import com.libraryman_api.book.Book; -import com.libraryman_api.fine.Fines; import com.libraryman_api.exception.InvalidSortFieldException; import com.libraryman_api.exception.ResourceNotFoundException; import com.libraryman_api.fine.FineRepository; +import com.libraryman_api.fine.Fines; import com.libraryman_api.member.MemberService; import com.libraryman_api.member.Members; import com.libraryman_api.member.dto.MembersDto; import com.libraryman_api.notification.NotificationService; - import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.mapping.PropertyReferenceException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; + import java.math.BigDecimal; import java.time.temporal.ChronoUnit; import java.util.Calendar; @@ -53,16 +53,16 @@ public class BorrowingService { * Constructs a new {@code BorrowingService} with the specified repositories and services. * * @param borrowingRepository the repository for managing borrowing records - * @param fineRepository the repository for managing fine records + * @param fineRepository the repository for managing fine records * @param notificationService the service for sending notifications - * @param bookService the service for managing book records + * @param bookService the service for managing book records */ public BorrowingService(BorrowingRepository borrowingRepository, FineRepository fineRepository, NotificationService notificationService, BookService bookService, MemberService memberService) { this.borrowingRepository = borrowingRepository; this.fineRepository = fineRepository; this.notificationService = notificationService; this.bookService = bookService; - this.memberService=memberService; + this.memberService = memberService; } /** @@ -150,8 +150,8 @@ public synchronized BorrowingsDto returnBook(int borrowingId) { BorrowingsDto borrowingsDto = getBorrowingById(borrowingId) .orElseThrow(() -> new ResourceNotFoundException("Borrowing not found")); Optional memberDto = memberService.getMemberById(borrowingsDto.getMember().getMemberId()); - if(!memberDto.isPresent()){ - throw new ResourceNotFoundException("Member not found"); + if (!memberDto.isPresent()) { + throw new ResourceNotFoundException("Member not found"); } if (borrowingsDto.getReturnDate() != null) { throw new ResourceNotFoundException("Book has already been returned"); @@ -202,7 +202,7 @@ public String payFine(int borrowingId) { BorrowingsDto borrowingsDto = getBorrowingById(borrowingId) .orElseThrow(() -> new ResourceNotFoundException("Borrowing not found")); Optional memberDto = memberService.getMemberById(borrowingsDto.getMember().getMemberId()); - if(!memberDto.isPresent()){ + if (!memberDto.isPresent()) { throw new ResourceNotFoundException("Member not found"); } Fines fine = borrowingsDto.getFine(); @@ -225,8 +225,8 @@ public String payFine(int borrowingId) { * If there are not enough copies available for a removal operation, or if the book is not found, * a {@link ResourceNotFoundException} is thrown.

* - * @param bookId the ID of the book to update - * @param operation the operation to perform ("ADD" to increase, "REMOVE" to decrease) + * @param bookId the ID of the book to update + * @param operation the operation to perform ("ADD" to increase, "REMOVE" to decrease) * @param numberOfCopies the number of copies to add or remove * @throws ResourceNotFoundException if the book is not found or if there are not enough copies to remove */ @@ -281,14 +281,14 @@ private BigDecimal calculateFineAmount(Borrowings borrowing) { * * @param memberId the ID of the member whose borrowings are to be retrieved * @param pageable the pagination information, including the page number and size + * @return a {@link Page} of {@link Borrowings} representing all borrowing associated with a specific member * @throws ResourceNotFoundException if the member has not borrowed any books * @throws InvalidSortFieldException if an invalid sortBy field is specified - * @return a {@link Page} of {@link Borrowings} representing all borrowing associated with a specific member */ public Page getAllBorrowingsOfMember(int memberId, Pageable pageable) { try { Page borrowings = borrowingRepository.findByMember_memberId(memberId, pageable); - + if (borrowings.isEmpty()) { throw new ResourceNotFoundException("Member didn't borrow any book"); } @@ -297,6 +297,7 @@ public Page getAllBorrowingsOfMember(int memberId, Pageable pagea throw new InvalidSortFieldException("The specified 'sortBy' value is invalid."); } } + /** * Converts a BorrowingsDto object to a Borrowings entity. * @@ -311,7 +312,7 @@ public Page getAllBorrowingsOfMember(int memberId, Pageable pagea */ - public Borrowings DtoToEntity(BorrowingsDto borrowingsDto){ + public Borrowings DtoToEntity(BorrowingsDto borrowingsDto) { Borrowings borrowings = new Borrowings(); borrowings.setBorrowDate(borrowingsDto.getBorrowDate()); borrowings.setMember(memberService.DtoEntity(borrowingsDto.getMember())); @@ -322,6 +323,7 @@ public Borrowings DtoToEntity(BorrowingsDto borrowingsDto){ borrowings.setBorrowingId(borrowingsDto.getBorrowingId()); return borrowings; } + /** * Converts a Borrowings entity to a BorrowingsDto object. * @@ -336,7 +338,7 @@ public Borrowings DtoToEntity(BorrowingsDto borrowingsDto){ */ - public BorrowingsDto EntityToDto(Borrowings borrowings){ + public BorrowingsDto EntityToDto(Borrowings borrowings) { BorrowingsDto borrowingsDto = new BorrowingsDto(); borrowingsDto.setBorrowingId(borrowings.getBorrowingId()); borrowingsDto.setFine(borrowings.getFine()); diff --git a/src/main/java/com/libraryman_api/borrowing/Borrowings.java b/src/main/java/com/libraryman_api/borrowing/Borrowings.java index 547f1ac..cff70dd 100644 --- a/src/main/java/com/libraryman_api/borrowing/Borrowings.java +++ b/src/main/java/com/libraryman_api/borrowing/Borrowings.java @@ -30,7 +30,7 @@ public class Borrowings { @JoinColumn(name = "member_id", nullable = false) private Members member; - @Column(name = "borrow_date",nullable = false) + @Column(name = "borrow_date", nullable = false) private Date borrowDate; @Column(name = "due_date", nullable = false) @@ -63,6 +63,10 @@ public int getBorrowingId() { return borrowingId; } + public void setBorrowingId(int borrowingId) { + this.borrowingId = borrowingId; + } + public Book getBook() { return book; } @@ -75,8 +79,6 @@ public Members getMember() { return member; } - public void setBorrowingId(int borrowingId) {this.borrowingId = borrowingId;} - public void setMember(Members member) { this.member = member; } diff --git a/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java b/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java index 7c1b392..af9c859 100644 --- a/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java +++ b/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java @@ -9,23 +9,14 @@ public class BorrowingsDto { private int borrowingId; - - private BookDto book; - - private Fines fine; - - private MembersDto member; - private Date borrowDate; - - private Date dueDate; + private Date returnDate; - private Date returnDate; public BorrowingsDto(int borrowingId, BookDto book, Fines fine, MembersDto member, Date borrowDate, Date dueDate, Date returnDate) { this.borrowingId = borrowingId; diff --git a/src/main/java/com/libraryman_api/email/EmailService.java b/src/main/java/com/libraryman_api/email/EmailService.java index 288723f..f5f80b3 100644 --- a/src/main/java/com/libraryman_api/email/EmailService.java +++ b/src/main/java/com/libraryman_api/email/EmailService.java @@ -1,15 +1,18 @@ package com.libraryman_api.email; +import com.libraryman_api.notification.NotificationRepository; import com.libraryman_api.notification.NotificationStatus; import com.libraryman_api.notification.Notifications; -import com.libraryman_api.notification.NotificationRepository; + import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.beans.factory.annotation.Value; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -20,11 +23,9 @@ @Service public class EmailService implements EmailSender { + private static final Logger LOGGER = LoggerFactory.getLogger(EmailService.class); private final NotificationRepository notificationRepository; private final JavaMailSender mailSender; - - private static final Logger LOGGER = LoggerFactory.getLogger(EmailService.class); - @Value("${spring.mail.properties.domain_name}") private String domainName; @@ -32,7 +33,7 @@ public class EmailService implements EmailSender { * Constructs a new {@code EmailService} with the specified {@link NotificationRepository} and {@link JavaMailSender}. * * @param notificationRepository the repository for managing notification entities - * @param mailSender the mail sender for sending email messages + * @param mailSender the mail sender for sending email messages */ public EmailService(NotificationRepository notificationRepository, JavaMailSender mailSender) { this.notificationRepository = notificationRepository; @@ -44,9 +45,9 @@ public EmailService(NotificationRepository notificationRepository, JavaMailSende * If the email is successfully sent, the notification status is updated to SENT. * If the email fails to send, the notification status is updated to FAILED and an exception is thrown. * - * @param to the recipient's email address - * @param email the content of the email to send - * @param subject the subject of the email + * @param to the recipient's email address + * @param email the content of the email to send + * @param subject the subject of the email * @param notification the {@link Notifications} object representing the email notification */ @Override diff --git a/src/main/java/com/libraryman_api/exception/ErrorDetails.java b/src/main/java/com/libraryman_api/exception/ErrorDetails.java index 29e6112..b0055cd 100644 --- a/src/main/java/com/libraryman_api/exception/ErrorDetails.java +++ b/src/main/java/com/libraryman_api/exception/ErrorDetails.java @@ -12,7 +12,7 @@ *
  • An equals() method that checks for equality based on the components.
  • *
  • A hashCode() method that calculates a hash code based on the components.
  • * - * + *

    * For the ErrorDetails record, the compiler will automatically generate the following members: *

    * Private final fields: diff --git a/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java b/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java index 6e637e7..3ef1ca0 100644 --- a/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java @@ -16,57 +16,57 @@ @ControllerAdvice public class GlobalExceptionHandler { - /** - * Handles {@link ResourceNotFoundException} exceptions. This method is - * triggered when a {@code ResourceNotFoundException} is thrown in the - * application. It constructs an {@link ErrorDetails} object containing the - * exception details and returns a {@link ResponseEntity} with an HTTP status of - * {@code 404 Not Found}. - * - * @param ex the exception that was thrown. - * @param request the current web request in which the exception was thrown. - * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an - * HTTP status of {@code 404 Not Found}. - */ - @ExceptionHandler(ResourceNotFoundException.class) - public ResponseEntity resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { - ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); - return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND); - } + /** + * Handles {@link ResourceNotFoundException} exceptions. This method is + * triggered when a {@code ResourceNotFoundException} is thrown in the + * application. It constructs an {@link ErrorDetails} object containing the + * exception details and returns a {@link ResponseEntity} with an HTTP status of + * {@code 404 Not Found}. + * + * @param ex the exception that was thrown. + * @param request the current web request in which the exception was thrown. + * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an + * HTTP status of {@code 404 Not Found}. + */ + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) { + ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); + return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND); + } - /** - * Handles {@link InvalidSortFieldException} exceptions. This method is - * triggered when an {@code InvalidSortFieldException} is thrown in the - * application. It constructs an {@link ErrorDetails} object containing the - * exception details and returns a {@link ResponseEntity} with an HTTP status of - * {@code 400 Bad Request}. - * - * @param ex the exception that was thrown. - * @param request the current web request in which the exception was thrown. - * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an - * HTTP status of {@code 400 Bad Request}. - */ - @ExceptionHandler(InvalidSortFieldException.class) - public ResponseEntity invalidSortFieldException(InvalidSortFieldException ex, WebRequest request) { - ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); - return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); - } + /** + * Handles {@link InvalidSortFieldException} exceptions. This method is + * triggered when an {@code InvalidSortFieldException} is thrown in the + * application. It constructs an {@link ErrorDetails} object containing the + * exception details and returns a {@link ResponseEntity} with an HTTP status of + * {@code 400 Bad Request}. + * + * @param ex the exception that was thrown. + * @param request the current web request in which the exception was thrown. + * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an + * HTTP status of {@code 400 Bad Request}. + */ + @ExceptionHandler(InvalidSortFieldException.class) + public ResponseEntity invalidSortFieldException(InvalidSortFieldException ex, WebRequest request) { + ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); + return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); + } - /** - * Handles {@link InvalidPasswordException} exceptions. This method is triggered - * when an {@code InvalidPasswordException} is thrown in the application. It - * constructs an {@link ErrorDetails} object containing the exception details - * and returns a {@link ResponseEntity} with an HTTP status of - * {@code 400 Bad Request}. - * - * @param ex the exception that was thrown. - * @param request the current web request in which the exception was thrown. - * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an - * HTTP status of {@code 400 Bad Request}. - */ - @ExceptionHandler(InvalidPasswordException.class) - public ResponseEntity invalidPasswordException(InvalidPasswordException ex, WebRequest request) { - ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); - return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); - } + /** + * Handles {@link InvalidPasswordException} exceptions. This method is triggered + * when an {@code InvalidPasswordException} is thrown in the application. It + * constructs an {@link ErrorDetails} object containing the exception details + * and returns a {@link ResponseEntity} with an HTTP status of + * {@code 400 Bad Request}. + * + * @param ex the exception that was thrown. + * @param request the current web request in which the exception was thrown. + * @return a {@link ResponseEntity} containing the {@link ErrorDetails} and an + * HTTP status of {@code 400 Bad Request}. + */ + @ExceptionHandler(InvalidPasswordException.class) + public ResponseEntity invalidPasswordException(InvalidPasswordException ex, WebRequest request) { + ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false)); + return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); + } } diff --git a/src/main/java/com/libraryman_api/exception/InvalidPasswordException.java b/src/main/java/com/libraryman_api/exception/InvalidPasswordException.java index 27013c0..2b4a5a8 100644 --- a/src/main/java/com/libraryman_api/exception/InvalidPasswordException.java +++ b/src/main/java/com/libraryman_api/exception/InvalidPasswordException.java @@ -13,15 +13,15 @@ public class InvalidPasswordException extends RuntimeException { * The {@code serialVersionUID} is a unique identifier for each version of a serializable class. * It is used during the deserialization process to verify that the sender and receiver of a * serialized object have loaded classes for that object that are compatible with each other. - * + *

    * The {@code serialVersionUID} field is important for ensuring that a serialized class * (especially when transmitted over a network or saved to disk) can be successfully deserialized, * even if the class definition changes in later versions. If the {@code serialVersionUID} does not * match during deserialization, an {@code InvalidClassException} is thrown. - * + *

    * This field is optional, but it is good practice to explicitly declare it to prevent * automatic generation, which could lead to compatibility issues when the class structure changes. - * + *

    * The {@code @Serial} annotation is used here to indicate that this field is related to * serialization. This annotation is available starting from Java 14 and helps improve clarity * regarding the purpose of this field. diff --git a/src/main/java/com/libraryman_api/exception/InvalidSortFieldException.java b/src/main/java/com/libraryman_api/exception/InvalidSortFieldException.java index e757a40..03d89f4 100644 --- a/src/main/java/com/libraryman_api/exception/InvalidSortFieldException.java +++ b/src/main/java/com/libraryman_api/exception/InvalidSortFieldException.java @@ -14,15 +14,15 @@ public class InvalidSortFieldException extends RuntimeException { * The {@code serialVersionUID} is a unique identifier for each version of a serializable class. * It is used during the deserialization process to verify that the sender and receiver of a * serialized object have loaded classes for that object that are compatible with each other. - * + *

    * The {@code serialVersionUID} field is important for ensuring that a serialized class * (especially when transmitted over a network or saved to disk) can be successfully deserialized, * even if the class definition changes in later versions. If the {@code serialVersionUID} does not * match during deserialization, an {@code InvalidClassException} is thrown. - * + *

    * This field is optional, but it is good practice to explicitly declare it to prevent * automatic generation, which could lead to compatibility issues when the class structure changes. - * + *

    * The {@code @Serial} annotation is used here to indicate that this field is related to * serialization. This annotation is available starting from Java 14 and helps improve clarity * regarding the purpose of this field. diff --git a/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java b/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java index 56a5840..a0242c3 100644 --- a/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java +++ b/src/main/java/com/libraryman_api/exception/ResourceNotFoundException.java @@ -14,15 +14,15 @@ public class ResourceNotFoundException extends RuntimeException { * The {@code serialVersionUID} is a unique identifier for each version of a serializable class. * It is used during the deserialization process to verify that the sender and receiver of a * serialized object have loaded classes for that object that are compatible with each other. - * + *

    * The {@code serialVersionUID} field is important for ensuring that a serialized class * (especially when transmitted over a network or saved to disk) can be successfully deserialized, * even if the class definition changes in later versions. If the {@code serialVersionUID} does not * match during deserialization, an {@code InvalidClassException} is thrown. - * + *

    * This field is optional, but it is good practice to explicitly declare it to prevent * automatic generation, which could lead to compatibility issues when the class structure changes. - * + *

    * The {@code @Serial} annotation is used here to indicate that this field is related to * serialization. This annotation is available starting from Java 14 and helps improve clarity * regarding the purpose of this field. diff --git a/src/main/java/com/libraryman_api/fine/Fines.java b/src/main/java/com/libraryman_api/fine/Fines.java index 48e06c2..ca0a674 100644 --- a/src/main/java/com/libraryman_api/fine/Fines.java +++ b/src/main/java/com/libraryman_api/fine/Fines.java @@ -15,11 +15,12 @@ public class Fines { @Column(name = "fine_id") private int fineId; -/** - * precision = 10 means the total number of digits (including decimal places) is 10. - * scale = 2 means the number of decimal places is 2. - * @Column(nullable = false, precision = 10, scale = 2) - * */ + /** + * precision = 10 means the total number of digits (including decimal places) is 10. + * scale = 2 means the number of decimal places is 2. + * + * @Column(nullable = false, precision = 10, scale = 2) + */ @Column(nullable = false, scale = 2) private BigDecimal amount; @@ -29,9 +30,7 @@ public class Fines { public Fines() { } - - public Fines( BigDecimal amount, boolean paid) { - + public Fines(BigDecimal amount, boolean paid) { this.amount = amount; this.paid = paid; } diff --git a/src/main/java/com/libraryman_api/member/MemberController.java b/src/main/java/com/libraryman_api/member/MemberController.java index 20a729a..a628d80 100644 --- a/src/main/java/com/libraryman_api/member/MemberController.java +++ b/src/main/java/com/libraryman_api/member/MemberController.java @@ -1,5 +1,9 @@ package com.libraryman_api.member; +import com.libraryman_api.exception.ResourceNotFoundException; +import com.libraryman_api.member.dto.MembersDto; +import com.libraryman_api.member.dto.UpdateMembersDto; +import com.libraryman_api.member.dto.UpdatePasswordDto; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -7,19 +11,7 @@ import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -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.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.libraryman_api.exception.ResourceNotFoundException; -import com.libraryman_api.member.dto.MembersDto; -import com.libraryman_api.member.dto.UpdateMembersDto; -import com.libraryman_api.member.dto.UpdatePasswordDto; +import org.springframework.web.bind.annotation.*; /** * REST controller for managing library members. @@ -44,28 +36,28 @@ public MemberController(MemberService memberService) { * Retrieves a paginated and sorted list of all library members. * * @param pageable contains pagination information (page number, size, and sorting). - * @param sortBy (optional) the field by which to sort the results. - * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. + * @param sortBy (optional) the field by which to sort the results. + * @param sortDir (optional) the direction of sorting (asc or desc). Defaults to ascending. * @return a {@link Page} of {@link Members} representing all members in the library. - * The results are sorted by name by default and limited to 5 members per page. + * The results are sorted by name by default and limited to 5 members per page. */ @GetMapping @PreAuthorize("hasRole('LIBRARIAN') or hasRole('ADMIN')") - public Page getAllMembers(@PageableDefault(page=0, size=5, sort="name") Pageable pageable, - @RequestParam(required = false) String sortBy, - @RequestParam(required = false) String sortDir) { - - // Adjust the pageable based on dynamic sorting parameters - if(sortBy!=null && !sortBy.isEmpty()) { - Sort.Direction direction= Sort.Direction.ASC; // Default direction - - if(sortDir!=null && sortDir.equalsIgnoreCase("desc")) { - direction = Sort.Direction.DESC; - } + public Page getAllMembers(@PageableDefault(page = 0, size = 5, sort = "name") Pageable pageable, + @RequestParam(required = false) String sortBy, + @RequestParam(required = false) String sortDir) { + + // Adjust the pageable based on dynamic sorting parameters + if (sortBy != null && !sortBy.isEmpty()) { + Sort.Direction direction = Sort.Direction.ASC; // Default direction + + if (sortDir != null && sortDir.equalsIgnoreCase("desc")) { + direction = Sort.Direction.DESC; + } + + pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)); + } - pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(direction, sortBy)) ; - } - return memberService.getAllMembers(pageable); } @@ -88,7 +80,7 @@ public ResponseEntity getMemberById(@PathVariable int id) { * Updates an existing library member. * If the member is not found, a {@link ResourceNotFoundException} is thrown. * - * @param id the ID of the member to update + * @param id the ID of the member to update * @param membersDtoDetails the {@link Members} object containing the updated details * @return the updated {@link Members} object */ @@ -109,19 +101,19 @@ public MembersDto updateMember(@PathVariable int id, @RequestBody UpdateMembersD public void deleteMember(@PathVariable int id) { memberService.deleteMember(id); } - + /** * Updates the password for a library member. * If the member is not found or the update fails, an appropriate exception will be thrown. * - * @param id the ID of the member whose password is to be updated + * @param id the ID of the member whose password is to be updated * @param updatePasswordDto the {@link UpdatePasswordDto} object containing the password details * @return a {@link ResponseEntity} containing a success message indicating the password was updated successfully */ @PutMapping("/{id}/password") @PreAuthorize("#id == authentication.principal.memberId") - public ResponseEntity updatePassword(@PathVariable int id, - @RequestBody UpdatePasswordDto updatePasswordDto) { + public ResponseEntity updatePassword(@PathVariable int id, + @RequestBody UpdatePasswordDto updatePasswordDto) { memberService.updatePassword(id, updatePasswordDto); return ResponseEntity.ok("Password updated successfully."); } diff --git a/src/main/java/com/libraryman_api/member/MemberService.java b/src/main/java/com/libraryman_api/member/MemberService.java index 8f053c8..553d650 100644 --- a/src/main/java/com/libraryman_api/member/MemberService.java +++ b/src/main/java/com/libraryman_api/member/MemberService.java @@ -1,14 +1,5 @@ package com.libraryman_api.member; -import java.util.Optional; - -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.mapping.PropertyReferenceException; -import org.springframework.stereotype.Service; - import com.libraryman_api.exception.InvalidPasswordException; import com.libraryman_api.exception.InvalidSortFieldException; import com.libraryman_api.exception.ResourceNotFoundException; @@ -17,7 +8,14 @@ import com.libraryman_api.member.dto.UpdatePasswordDto; import com.libraryman_api.notification.NotificationService; import com.libraryman_api.security.config.PasswordEncoder; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.mapping.PropertyReferenceException; +import org.springframework.stereotype.Service; +import java.util.Optional; /** @@ -45,7 +43,7 @@ public class MemberService { /** * Constructs a new {@code MemberService} with the specified repositories and services. * - * @param memberRepository the repository for managing member records + * @param memberRepository the repository for managing member records * @param notificationService the service for sending notifications related to member activities */ public MemberService(MemberRepository memberRepository, NotificationService notificationService, PasswordEncoder passwordEncoder) { @@ -96,7 +94,7 @@ public Optional getMemberById(int memberId) { public MembersDto addMember(MembersDto membersDto) { Members member = DtoEntity(membersDto); Members currentMember = memberRepository.save(member); - if(currentMember!=null) + if (currentMember != null) notificationService.accountCreatedNotification(currentMember); return EntityToDto(currentMember); @@ -109,7 +107,7 @@ public MembersDto addMember(MembersDto membersDto) { * {@link ResourceNotFoundException} if the member is not found. After updating, * a notification about the account details update is sent.

    * - * @param memberId the ID of the member to update + * @param memberId the ID of the member to update * @param membersDtoDetails the updated member details * @return the updated member record * @throws ResourceNotFoundException if the member is not found @@ -123,7 +121,7 @@ public MembersDto updateMember(int memberId, UpdateMembersDto membersDtoDetails) member.setUsername(membersDtoDetails.getUsername()); member.setEmail(membersDtoDetails.getEmail()); member = memberRepository.save(member); - if(member!=null) + if (member != null) notificationService.accountDetailsUpdateNotification(member); return EntityToDto(member); } @@ -149,31 +147,31 @@ public void deleteMember(int memberId) { notificationService.accountDeletionNotification(member); memberRepository.delete(member); } - + /** * Updates the password for a library member. * - *

    This method verifies the current password provided by the member, checks if the - * new password is different, and then updates the member's password in the database. - * If the current password is incorrect or the new password is the same as the current + *

    This method verifies the current password provided by the member, checks if the + * new password is different, and then updates the member's password in the database. + * If the current password is incorrect or the new password is the same as the current * password, an {@link InvalidPasswordException} is thrown.

    * - * @param memberId the ID of the member whose password is to be updated + * @param memberId the ID of the member whose password is to be updated * @param updatePasswordDto the {@link UpdatePasswordDto} object containing the password details * @throws ResourceNotFoundException if the member with the specified ID is not found - * @throws InvalidPasswordException if the current password is incorrect or the new password is the same as the current password + * @throws InvalidPasswordException if the current password is incorrect or the new password is the same as the current password */ public void updatePassword(int memberId, UpdatePasswordDto updatePasswordDto) { Members member = memberRepository.findById(memberId) .orElseThrow(() -> new ResourceNotFoundException("Member not found")); // Check the current password - String currentAuthPassword = member.getPassword(); + String currentAuthPassword = member.getPassword(); if (!passwordEncoder.bCryptPasswordEncoder().matches(updatePasswordDto.getCurrentPassword(), currentAuthPassword)) { throw new InvalidPasswordException("Current password is incorrect"); } - + // Check if new password is different from old password if (updatePasswordDto.getCurrentPassword().equals(updatePasswordDto.getNewPassword())) { throw new InvalidPasswordException("New password must be different from the old password"); @@ -182,7 +180,7 @@ public void updatePassword(int memberId, UpdatePasswordDto updatePasswordDto) { member.setPassword(passwordEncoder.bCryptPasswordEncoder().encode(updatePasswordDto.getNewPassword())); memberRepository.save(member); } - + /** * Converts a MembersDto object to a Members entity. * @@ -193,8 +191,8 @@ public void updatePassword(int memberId, UpdatePasswordDto updatePasswordDto) { * @param membersDto the DTO object containing member information * @return a Members entity with data populated from the DTO */ - public Members DtoEntity(MembersDto membersDto){ - Members members= new Members(); + public Members DtoEntity(MembersDto membersDto) { + Members members = new Members(); members.setMemberId(membersDto.getMemberId()); members.setRole(membersDto.getRole()); members.setName(membersDto.getName()); @@ -204,7 +202,7 @@ public Members DtoEntity(MembersDto membersDto){ members.setMembershipDate(membersDto.getMembershipDate()); return members; } - + /** * Converts a Members entity to a MembersDto object. * @@ -216,8 +214,8 @@ public Members DtoEntity(MembersDto membersDto){ * @param members the entity object containing member information * @return a MembersDto object with data populated from the entity */ - public MembersDto EntityToDto(Members members){ - MembersDto membersDto= new MembersDto(); + public MembersDto EntityToDto(Members members) { + MembersDto membersDto = new MembersDto(); membersDto.setMemberId(members.getMemberId()); membersDto.setName(members.getName()); membersDto.setUsername(members.getUsername()); @@ -225,6 +223,6 @@ public MembersDto EntityToDto(Members members){ membersDto.setEmail(members.getEmail()); membersDto.setPassword(members.getPassword()); membersDto.setMembershipDate(members.getMembershipDate()); - return membersDto; + return membersDto; } } diff --git a/src/main/java/com/libraryman_api/member/Members.java b/src/main/java/com/libraryman_api/member/Members.java index aabdad3..f967f82 100644 --- a/src/main/java/com/libraryman_api/member/Members.java +++ b/src/main/java/com/libraryman_api/member/Members.java @@ -1,18 +1,17 @@ package com.libraryman_api.member; import jakarta.persistence.*; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Collections; import java.util.Date; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - @Entity -public class Members implements UserDetails{ +public class Members implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, @@ -28,7 +27,7 @@ public class Members implements UserDetails{ @Column(name = "username") private String username; - + @Column(unique = true, nullable = false) private String email; @@ -42,8 +41,6 @@ public class Members implements UserDetails{ @Column(name = "membership_date") private Date membershipDate; - - public Members() { } @@ -60,6 +57,10 @@ public int getMemberId() { return memberId; } + public void setMemberId(int memberId) { + this.memberId = memberId; + } + public String getName() { return name; } @@ -80,8 +81,6 @@ public String getPassword() { return password; } - public void setMemberId(int memberId) {this.memberId = memberId;} - public void setPassword(String password) { this.password = password; } @@ -102,18 +101,18 @@ public void setMembershipDate(Date membershipDate) { this.membershipDate = membershipDate; } - public String getUsername() { - return username; - } + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } - public void setUsername(String username) { - this.username = username; - } + @Override + public Collection getAuthorities() { + // TODO Auto-generated method stub + return Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role.name())); + } - @Override - public Collection getAuthorities() { - // TODO Auto-generated method stub - return Collections.singletonList(new SimpleGrantedAuthority("ROLE_"+role.name())); - } - } diff --git a/src/main/java/com/libraryman_api/member/Role.java b/src/main/java/com/libraryman_api/member/Role.java index e92e5a6..732332d 100644 --- a/src/main/java/com/libraryman_api/member/Role.java +++ b/src/main/java/com/libraryman_api/member/Role.java @@ -1,5 +1,5 @@ package com.libraryman_api.member; public enum Role { - ADMIN, LIBRARIAN, USER + ADMIN, LIBRARIAN, USER } diff --git a/src/main/java/com/libraryman_api/member/dto/MembersDto.java b/src/main/java/com/libraryman_api/member/dto/MembersDto.java index 7f97d8a..c08d655 100644 --- a/src/main/java/com/libraryman_api/member/dto/MembersDto.java +++ b/src/main/java/com/libraryman_api/member/dto/MembersDto.java @@ -1,25 +1,17 @@ package com.libraryman_api.member.dto; -import java.util.Date; - import com.libraryman_api.member.Role; +import java.util.Date; + public class MembersDto { private int memberId; - private String name; - private String username; - private String email; - private String password; - - private Role role; - - private Date membershipDate; public MembersDto(int memberId, String name, String username, String email, String password, Role role, Date membershipDate) { @@ -46,15 +38,15 @@ public void setMemberId(int memberId) { public String getName() { return name; } - - public String getUsername() { - return username; - } public void setName(String name) { this.name = name; } - + + public String getUsername() { + return username; + } + public void setUsername(String username) { this.username = username; } diff --git a/src/main/java/com/libraryman_api/member/dto/UpdateMembersDto.java b/src/main/java/com/libraryman_api/member/dto/UpdateMembersDto.java index 8859bb3..f76b715 100644 --- a/src/main/java/com/libraryman_api/member/dto/UpdateMembersDto.java +++ b/src/main/java/com/libraryman_api/member/dto/UpdateMembersDto.java @@ -3,9 +3,7 @@ public class UpdateMembersDto { private String name; - private String username; - private String email; public UpdateMembersDto(String name, String username, String email) { @@ -20,15 +18,15 @@ public UpdateMembersDto() { public String getName() { return name; } - - public String getUsername() { - return username; - } public void setName(String name) { this.name = name; } - + + public String getUsername() { + return username; + } + public void setUsername(String username) { this.username = username; } diff --git a/src/main/java/com/libraryman_api/member/dto/UpdatePasswordDto.java b/src/main/java/com/libraryman_api/member/dto/UpdatePasswordDto.java index a7be20a..00a124f 100644 --- a/src/main/java/com/libraryman_api/member/dto/UpdatePasswordDto.java +++ b/src/main/java/com/libraryman_api/member/dto/UpdatePasswordDto.java @@ -3,38 +3,37 @@ public class UpdatePasswordDto { private String currentPassword; - private String newPassword; public UpdatePasswordDto(String currentPassword, String newPassword) { - this.currentPassword = currentPassword; - this.newPassword = newPassword; - } + this.currentPassword = currentPassword; + this.newPassword = newPassword; + } public UpdatePasswordDto() { } - + public String getCurrentPassword() { - return currentPassword; - } + return currentPassword; + } - public void setCurrentPassword(String currentPassword) { - this.currentPassword = currentPassword; - } + public void setCurrentPassword(String currentPassword) { + this.currentPassword = currentPassword; + } - public String getNewPassword() { - return newPassword; - } + public String getNewPassword() { + return newPassword; + } - public void setNewPassword(String newPassword) { - this.newPassword = newPassword; - } + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } - @Override + @Override public String toString() { return "UpdatePasswordDto{" + - "currentPassword='" + currentPassword + '\'' + - ", newPassword='" + newPassword + '\'' + - '}'; + "currentPassword='" + currentPassword + '\'' + + ", newPassword='" + newPassword + '\'' + + '}'; } } diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java index 75412b5..aa099c0 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java @@ -1,6 +1,7 @@ package com.libraryman_api.newsletter; import jakarta.persistence.*; + import java.util.UUID; @Entity diff --git a/src/main/java/com/libraryman_api/notification/NotificationService.java b/src/main/java/com/libraryman_api/notification/NotificationService.java index a79b04b..cfb3d30 100644 --- a/src/main/java/com/libraryman_api/notification/NotificationService.java +++ b/src/main/java/com/libraryman_api/notification/NotificationService.java @@ -5,6 +5,8 @@ import com.libraryman_api.exception.ResourceNotFoundException; import com.libraryman_api.member.MemberRepository; import com.libraryman_api.member.Members; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -16,8 +18,6 @@ import java.util.Date; import java.util.List; import java.util.Optional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** @@ -29,10 +29,10 @@ */ @Service public class NotificationService { + private static final Logger LOGGER = LoggerFactory.getLogger(NotificationService.class); private final EmailSender emailSender; private final NotificationRepository notificationRepository; private final MemberRepository memberRepository; - private static final Logger LOGGER = LoggerFactory.getLogger(NotificationService.class); /** * Constructs a new {@code NotificationService} with the specified {@link EmailSender}, @@ -206,7 +206,7 @@ public void bookReturnedNotification(Borrowings borrowing) { */ private void sendNotification(Notifications notification) { emailSender.send( - notification.getMember().getEmail(), + notification.getMember().getEmail(), buildEmail( subject(notification.getNotificationType()), notification.getMember().getName(), @@ -220,7 +220,7 @@ private void sendNotification(Notifications notification) { // Scheduled method to send reminders for books due in 2 days @Scheduled(cron = "0 0 10 * * ?") // Runs every day at 10 AM - public void sendDueDateReminders(){ + public void sendDueDateReminders() { Calendar calendar = Calendar.getInstance(); // Get today's date @@ -237,10 +237,10 @@ public void sendDueDateReminders(){ for (Borrowings borrowing : borrowingsDueSoon) { try { Optional member = memberRepository.findByMemberId(borrowing.getMember().getMemberId()); - if(member.isPresent()) - reminderNotification(borrowing); + if (member.isPresent()) + reminderNotification(borrowing); } catch (ResourceNotFoundException e) { - LOGGER.error("Member not found for memberId: " + borrowing.getMember().getMemberId(), e); + LOGGER.error("Member not found for memberId: " + borrowing.getMember().getMemberId(), e); } } } diff --git a/src/main/java/com/libraryman_api/notification/NotificationType.java b/src/main/java/com/libraryman_api/notification/NotificationType.java index f2d4dc3..3dfdd41 100644 --- a/src/main/java/com/libraryman_api/notification/NotificationType.java +++ b/src/main/java/com/libraryman_api/notification/NotificationType.java @@ -5,27 +5,43 @@ * Represents the types of notifications available in the system. */ public enum NotificationType { - /** Used for account creation notifications. */ + /** + * Used for account creation notifications. + */ ACCOUNT_CREATED, - /** Used for account deletion notifications. */ + /** + * Used for account deletion notifications. + */ ACCOUNT_DELETED, - /** Sent when a user borrows a book. */ + /** + * Sent when a user borrows a book. + */ BORROW, - /** Sent to remind user for due date */ + /** + * Sent to remind user for due date + */ REMINDER, - /** Indicates that a user has paid a fine. */ + /** + * Indicates that a user has paid a fine. + */ PAID, - /** Imposed when a user incurs a fine. */ + /** + * Imposed when a user incurs a fine. + */ FINE, - /** Used when updating user details. */ + /** + * Used when updating user details. + */ UPDATE, - /** Sent when a user returns a borrowed book. */ + /** + * Sent when a user returns a borrowed book. + */ RETURNED } diff --git a/src/main/java/com/libraryman_api/notification/Notifications.java b/src/main/java/com/libraryman_api/notification/Notifications.java index 3ef9cb6..a7b948e 100644 --- a/src/main/java/com/libraryman_api/notification/Notifications.java +++ b/src/main/java/com/libraryman_api/notification/Notifications.java @@ -2,6 +2,7 @@ import com.libraryman_api.member.Members; import jakarta.persistence.*; + import java.sql.Timestamp; @Entity @@ -31,7 +32,6 @@ public class Notifications { private NotificationStatus notificationStatus; - public Notifications() { } diff --git a/src/main/java/com/libraryman_api/security/config/WebConfiguration.java b/src/main/java/com/libraryman_api/security/config/WebConfiguration.java index 47d2d60..61b4002 100644 --- a/src/main/java/com/libraryman_api/security/config/WebConfiguration.java +++ b/src/main/java/com/libraryman_api/security/config/WebConfiguration.java @@ -1,8 +1,8 @@ package com.libraryman_api.security.config; +import com.libraryman_api.security.jwt.JwtAuthenticationFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @@ -16,41 +16,42 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; -import com.libraryman_api.security.jwt.JwtAuthenticationFilter; - import static org.springframework.security.config.Customizer.withDefaults; import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; @Configuration -@EnableWebSecurity(debug = true) // Do not use (debug=true) in a production system! as this contain sensitive information. +@EnableWebSecurity(debug = true) +// Do not use (debug=true) in a production system! as this contain sensitive information. @EnableMethodSecurity(prePostEnabled = true) public class WebConfiguration { - private JwtAuthenticationFilter jwtFilter; - - public WebConfiguration(JwtAuthenticationFilter jwtFilter) { - this.jwtFilter=jwtFilter; - } + private final JwtAuthenticationFilter jwtFilter; + + public WebConfiguration(JwtAuthenticationFilter jwtFilter) { + this.jwtFilter = jwtFilter; + } + @Bean public SecurityFilterChain web(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests((request) -> request - // make sure it is in order to access the proper Url + // make sure it is in order to access the proper Url - .requestMatchers("/api/signup").permitAll() - .requestMatchers("/api/login").permitAll() - .requestMatchers("/api/logout").permitAll() - .anyRequest().authenticated() + .requestMatchers("/api/signup").permitAll() + .requestMatchers("/api/login").permitAll() + .requestMatchers("/api/logout").permitAll() + .anyRequest().authenticated() ) - .logout(logout->logout - .deleteCookies("LibraryManCookie")) + .logout(logout -> logout + .deleteCookies("LibraryManCookie")) .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) .formLogin(withDefaults()); - + http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) - .httpBasic(httpBasic -> {}); - + .httpBasic(httpBasic -> { + }); + http.oauth2Login(withDefaults()); return http.build(); } @@ -59,6 +60,7 @@ public SecurityFilterChain web(HttpSecurity http) throws Exception { public AuthenticationManager authenticationManager(AuthenticationConfiguration builder) throws Exception { return builder.getAuthenticationManager(); } + @Bean public CorsConfigurationSource corsConfigurationSource() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); diff --git a/src/main/java/com/libraryman_api/security/controllers/LoginController.java b/src/main/java/com/libraryman_api/security/controllers/LoginController.java index aed0c1e..20ee825 100644 --- a/src/main/java/com/libraryman_api/security/controllers/LoginController.java +++ b/src/main/java/com/libraryman_api/security/controllers/LoginController.java @@ -1,17 +1,15 @@ package com.libraryman_api.security.controllers; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - import com.libraryman_api.security.model.LoginRequest; import com.libraryman_api.security.model.LoginResponse; import com.libraryman_api.security.services.LoginService; - import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; @RestController public class LoginController { @@ -26,7 +24,7 @@ public LoginController(LoginService loginService) { public ResponseEntity login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) { LoginResponse loginResponse = loginService.login(loginRequest); - if (loginResponse != null) { + if (loginResponse != null) { setAuthCookie(response); return new ResponseEntity<>(loginResponse, HttpStatus.OK); } else { @@ -37,7 +35,7 @@ public ResponseEntity login(@RequestBody LoginRequest loginReques private void setAuthCookie(HttpServletResponse response) { Cookie cookie = new Cookie("LibraryManCookie", "libraryman_cookie"); cookie.setMaxAge(3600); // (3600 seconds) - cookie.setPath("/"); + cookie.setPath("/"); response.addCookie(cookie); } } diff --git a/src/main/java/com/libraryman_api/security/controllers/LogoutController.java b/src/main/java/com/libraryman_api/security/controllers/LogoutController.java index 20d4a92..03bb45c 100644 --- a/src/main/java/com/libraryman_api/security/controllers/LogoutController.java +++ b/src/main/java/com/libraryman_api/security/controllers/LogoutController.java @@ -3,7 +3,6 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; - import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; @@ -19,14 +18,14 @@ public ResponseEntity logout(HttpServletRequest request, HttpServletResp new SecurityContextLogoutHandler().logout(request, response, authentication); } removeAuthCookie(response); - + return ResponseEntity.ok("Successfully logged out."); } private void removeAuthCookie(HttpServletResponse response) { Cookie cookie = new Cookie("LibraryManCookie", null); - cookie.setMaxAge(0); - cookie.setPath("/"); + cookie.setMaxAge(0); + cookie.setPath("/"); cookie.setHttpOnly(true); cookie.setSecure(true); response.addCookie(cookie); diff --git a/src/main/java/com/libraryman_api/security/controllers/SignupController.java b/src/main/java/com/libraryman_api/security/controllers/SignupController.java index a1f6891..c7376d2 100644 --- a/src/main/java/com/libraryman_api/security/controllers/SignupController.java +++ b/src/main/java/com/libraryman_api/security/controllers/SignupController.java @@ -1,36 +1,37 @@ package com.libraryman_api.security.controllers; +import com.libraryman_api.member.Members; +import com.libraryman_api.security.services.SignupService; import org.springframework.security.access.prepost.PreAuthorize; -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 com.libraryman_api.member.Members; -import com.libraryman_api.security.services.SignupService; - @RestController public class SignupController { - - private SignupService signupService; - public SignupController(SignupService signupService) { - this.signupService=signupService; - } - - @PostMapping("/api/signup") - public void signup(@RequestBody Members members) { - this.signupService.signup(members); - - } - @PostMapping("/api/signup/admin") - @PreAuthorize("hasRole('ADMIN')") - public void signupAdmin(@RequestBody Members members) { - this.signupService.signupAdmin(members); - } - @PostMapping("/api/signup/librarian") - @PreAuthorize("hasRole('LIBRARIAN') or hasRole('ADMIN')") - public void signupLibrarian(@RequestBody Members members) { - this.signupService.signupLibrarian(members); - } + + private final SignupService signupService; + + public SignupController(SignupService signupService) { + this.signupService = signupService; + } + + @PostMapping("/api/signup") + public void signup(@RequestBody Members members) { + this.signupService.signup(members); + + } + + @PostMapping("/api/signup/admin") + @PreAuthorize("hasRole('ADMIN')") + public void signupAdmin(@RequestBody Members members) { + this.signupService.signupAdmin(members); + } + + @PostMapping("/api/signup/librarian") + @PreAuthorize("hasRole('LIBRARIAN') or hasRole('ADMIN')") + public void signupLibrarian(@RequestBody Members members) { + this.signupService.signupLibrarian(members); + } } diff --git a/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationFilter.java b/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationFilter.java index 875779b..f48ab45 100644 --- a/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationFilter.java +++ b/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationFilter.java @@ -1,9 +1,10 @@ package com.libraryman_api.security.jwt; -import java.io.IOException; - -import org.springframework.beans.factory.annotation.Autowired; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; @@ -12,51 +13,45 @@ import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { - - private JwtAuthenticationHelper jwtHelper; - - private UserDetailsService userDetailsService; - - public JwtAuthenticationFilter(JwtAuthenticationHelper jwtHelper,UserDetailsService userDetailsService) { - this.jwtHelper=jwtHelper; - this.userDetailsService=userDetailsService; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - - String requestHeader=request.getHeader("Authorization"); - String username=null; - String token=null; - if(requestHeader!=null && requestHeader.startsWith("Bearer ")) { - token=requestHeader.substring(7); - username=jwtHelper.getUsernameFromToken(token); - if(username!=null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails=userDetailsService.loadUserByUsername(username); - if(!(jwtHelper.isTokenExpired(token))) { - UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); - usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); - - } - else { - System.out.println("Token is expired or user details not found."); - } - } - - } - filterChain.doFilter(request, response); - } - - - - + + private final JwtAuthenticationHelper jwtHelper; + + private final UserDetailsService userDetailsService; + + public JwtAuthenticationFilter(JwtAuthenticationHelper jwtHelper, UserDetailsService userDetailsService) { + this.jwtHelper = jwtHelper; + this.userDetailsService = userDetailsService; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + String requestHeader = request.getHeader("Authorization"); + String username = null; + String token = null; + if (requestHeader != null && requestHeader.startsWith("Bearer ")) { + token = requestHeader.substring(7); + username = jwtHelper.getUsernameFromToken(token); + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + if (!(jwtHelper.isTokenExpired(token))) { + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); + + } else { + System.out.println("Token is expired or user details not found."); + } + } + + } + filterChain.doFilter(request, response); + } + + } diff --git a/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationHelper.java b/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationHelper.java index b44e446..d9dcc01 100644 --- a/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationHelper.java +++ b/src/main/java/com/libraryman_api/security/jwt/JwtAuthenticationHelper.java @@ -1,49 +1,48 @@ package com.libraryman_api.security.jwt; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import javax.crypto.spec.SecretKeySpec; - +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; +import javax.crypto.spec.SecretKeySpec; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; @Component public class JwtAuthenticationHelper { - private static final long JWT_TOKEN_VALIDITY=60*60; - @Value("${jwt.secretKey}") - private String secret; - public String getUsernameFromToken(String token) { - String username=getClaimsFromToken(token).getSubject(); - return username; - } - - public Claims getClaimsFromToken(String token) { - Claims claims=Jwts.parserBuilder() - .setSigningKey(secret.getBytes()) - .build().parseClaimsJws(token).getBody(); - return claims; - } - - public Boolean isTokenExpired(String token) { - Claims claims=getClaimsFromToken(token); - Date expDate=claims.getExpiration(); - return expDate.before(new Date()); - } - - public String generateToken(UserDetails userDetails) { - Map claims=new HashMap<>(); - return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()) - .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis()+JWT_TOKEN_VALIDITY*1000)) - .signWith(new SecretKeySpec(secret.getBytes(),SignatureAlgorithm.HS512.getJcaName()),SignatureAlgorithm.HS512) - .compact(); - } + private static final long JWT_TOKEN_VALIDITY = 60 * 60; + @Value("${jwt.secretKey}") + private String secret; + + public String getUsernameFromToken(String token) { + String username = getClaimsFromToken(token).getSubject(); + return username; + } + + public Claims getClaimsFromToken(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(secret.getBytes()) + .build().parseClaimsJws(token).getBody(); + return claims; + } + + public Boolean isTokenExpired(String token) { + Claims claims = getClaimsFromToken(token); + Date expDate = claims.getExpiration(); + return expDate.before(new Date()); + } + + public String generateToken(UserDetails userDetails) { + Map claims = new HashMap<>(); + return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) + .signWith(new SecretKeySpec(secret.getBytes(), SignatureAlgorithm.HS512.getJcaName()), SignatureAlgorithm.HS512) + .compact(); + } } diff --git a/src/main/java/com/libraryman_api/security/model/LoginRequest.java b/src/main/java/com/libraryman_api/security/model/LoginRequest.java index e77d1cd..126e032 100644 --- a/src/main/java/com/libraryman_api/security/model/LoginRequest.java +++ b/src/main/java/com/libraryman_api/security/model/LoginRequest.java @@ -2,27 +2,26 @@ public class LoginRequest { - private String username; - - private String password; + private String username; - + private String password; - public String getUsername() { - return username; - } - public void setUsername(String username) { - this.username = username; - } + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } - public String getPassword() { - return password; - } - public void setPassword(String password) { - this.password = password; - } - - } diff --git a/src/main/java/com/libraryman_api/security/model/LoginResponse.java b/src/main/java/com/libraryman_api/security/model/LoginResponse.java index 2b53d41..a3730bf 100644 --- a/src/main/java/com/libraryman_api/security/model/LoginResponse.java +++ b/src/main/java/com/libraryman_api/security/model/LoginResponse.java @@ -3,19 +3,19 @@ public class LoginResponse { - private String token; - - public LoginResponse(String token) { - this.token=token; - } - public String getToken() { - return token; - } - - public void setToken(String token) { - this.token = token; - } - - - + private String token; + + public LoginResponse(String token) { + this.token = token; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + } diff --git a/src/main/java/com/libraryman_api/security/services/CustomUserDetailsService.java b/src/main/java/com/libraryman_api/security/services/CustomUserDetailsService.java index 2c66c82..dbd525f 100644 --- a/src/main/java/com/libraryman_api/security/services/CustomUserDetailsService.java +++ b/src/main/java/com/libraryman_api/security/services/CustomUserDetailsService.java @@ -1,22 +1,22 @@ package com.libraryman_api.security.services; +import com.libraryman_api.member.MemberRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import com.libraryman_api.member.MemberRepository; @Service -public class CustomUserDetailsService implements UserDetailsService{ +public class CustomUserDetailsService implements UserDetailsService { + + @Autowired + MemberRepository memberRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - @Autowired - MemberRepository memberRepository; - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - - return memberRepository.findByUsername(username).orElseThrow(()-> new UsernameNotFoundException("Username not Found")); - } + return memberRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("Username not Found")); + } } diff --git a/src/main/java/com/libraryman_api/security/services/LoginService.java b/src/main/java/com/libraryman_api/security/services/LoginService.java index c255c4e..8666cec 100644 --- a/src/main/java/com/libraryman_api/security/services/LoginService.java +++ b/src/main/java/com/libraryman_api/security/services/LoginService.java @@ -1,5 +1,9 @@ package com.libraryman_api.security.services; +import com.libraryman_api.member.MemberRepository; +import com.libraryman_api.security.jwt.JwtAuthenticationHelper; +import com.libraryman_api.security.model.LoginRequest; +import com.libraryman_api.security.model.LoginResponse; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -7,45 +11,39 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; -import com.libraryman_api.member.MemberRepository; -import com.libraryman_api.security.jwt.JwtAuthenticationHelper; -import com.libraryman_api.security.model.LoginRequest; -import com.libraryman_api.security.model.LoginResponse; - @Service public class LoginService { - private AuthenticationManager authenticationManager; - - private UserDetailsService userDetailsService; - - private JwtAuthenticationHelper jwtHelper; - - private MemberRepository memberRepository; - - public LoginService(AuthenticationManager authenticationManager,UserDetailsService userDetailsService,JwtAuthenticationHelper jwtHelper,MemberRepository memberRepository) { - this.authenticationManager=authenticationManager; - this.userDetailsService=userDetailsService; - this.jwtHelper=jwtHelper; - this.memberRepository=memberRepository; - } - - public LoginResponse login(LoginRequest loginRequest) { - Authenticate(loginRequest.getUsername(), loginRequest.getPassword()); - UserDetails userDetails=userDetailsService.loadUserByUsername(loginRequest.getUsername()); - String token=jwtHelper.generateToken(userDetails); - LoginResponse loginResponse=new LoginResponse(token); - return loginResponse; - } - - public void Authenticate(String username,String password) { - UsernamePasswordAuthenticationToken authenticateToken=new UsernamePasswordAuthenticationToken(username, password); - try { - authenticationManager.authenticate(authenticateToken); - } - catch(BadCredentialsException e){ - throw new BadCredentialsException("Invalid Username or Password"); - } - } + private final AuthenticationManager authenticationManager; + + private final UserDetailsService userDetailsService; + + private final JwtAuthenticationHelper jwtHelper; + + private final MemberRepository memberRepository; + + public LoginService(AuthenticationManager authenticationManager, UserDetailsService userDetailsService, JwtAuthenticationHelper jwtHelper, MemberRepository memberRepository) { + this.authenticationManager = authenticationManager; + this.userDetailsService = userDetailsService; + this.jwtHelper = jwtHelper; + this.memberRepository = memberRepository; + } + + public LoginResponse login(LoginRequest loginRequest) { + Authenticate(loginRequest.getUsername(), loginRequest.getPassword()); + UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername()); + String token = jwtHelper.generateToken(userDetails); + LoginResponse loginResponse = new LoginResponse(token); + return loginResponse; + } + + public void Authenticate(String username, String password) { + UsernamePasswordAuthenticationToken authenticateToken = new UsernamePasswordAuthenticationToken(username, password); + try { + authenticationManager.authenticate(authenticateToken); + } catch (BadCredentialsException e) { + throw new BadCredentialsException("Invalid Username or Password"); + } + } } diff --git a/src/main/java/com/libraryman_api/security/services/SignupService.java b/src/main/java/com/libraryman_api/security/services/SignupService.java index 33ff122..cb7bb64 100644 --- a/src/main/java/com/libraryman_api/security/services/SignupService.java +++ b/src/main/java/com/libraryman_api/security/services/SignupService.java @@ -1,88 +1,87 @@ package com.libraryman_api.security.services; -import java.util.Date; -import java.util.Optional; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - - import com.libraryman_api.exception.ResourceNotFoundException; import com.libraryman_api.member.MemberRepository; import com.libraryman_api.member.Members; import com.libraryman_api.member.Role; import com.libraryman_api.security.config.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.Optional; @Service public class SignupService { - private MemberRepository memberRepository; + private final MemberRepository memberRepository; + + private final PasswordEncoder passwordEncoder; + + + public SignupService(MemberRepository memberRepository, PasswordEncoder passwordEncoder) { + this.memberRepository = memberRepository; + this.passwordEncoder = passwordEncoder; + } + + public void signup(Members members) { + Optional memberOptId = memberRepository.findById(members.getMemberId()); + Optional memberOptUsername = memberRepository.findByUsername(members.getUsername()); + if (memberOptId.isPresent()) { + throw new ResourceNotFoundException("User already Exists"); + } + if (memberOptUsername.isPresent()) { + throw new ResourceNotFoundException("User already Exists"); + } + String encoded_password = passwordEncoder.bCryptPasswordEncoder().encode(members.getPassword()); + Members new_members = new Members(); + new_members.setEmail(members.getEmail()); + new_members.setName(members.getName()); + new_members.setPassword(encoded_password); + new_members.setRole(Role.USER); + new_members.setMembershipDate(new Date()); + new_members.setUsername(members.getUsername()); + memberRepository.save(new_members); + } + + public void signupAdmin(Members members) { + Optional memberOptId = memberRepository.findById(members.getMemberId()); + Optional memberOptUsername = memberRepository.findByUsername(members.getUsername()); + if (memberOptId.isPresent()) { + throw new ResourceNotFoundException("User already Exists"); + } + if (memberOptUsername.isPresent()) { + throw new ResourceNotFoundException("User already Exists"); + } + + String encoded_password = passwordEncoder.bCryptPasswordEncoder().encode(members.getPassword()); + Members new_members = new Members(); + new_members.setEmail(members.getEmail()); + new_members.setName(members.getName()); + new_members.setPassword(encoded_password); + new_members.setRole(Role.ADMIN); + new_members.setMembershipDate(new Date()); + new_members.setUsername(members.getUsername()); + memberRepository.save(new_members); + + } - private PasswordEncoder passwordEncoder; - - - public SignupService(MemberRepository memberRepository,PasswordEncoder passwordEncoder) { - this.memberRepository=memberRepository; - this.passwordEncoder=passwordEncoder; - } - public void signup(Members members) { - Optional memberOptId=memberRepository.findById(members.getMemberId()); - Optional memberOptUsername=memberRepository.findByUsername(members.getUsername()); - if(memberOptId.isPresent()) { - throw new ResourceNotFoundException("User already Exists"); - } - if(memberOptUsername.isPresent()) { - throw new ResourceNotFoundException("User already Exists"); - } - String encoded_password=passwordEncoder.bCryptPasswordEncoder().encode(members.getPassword()); - Members new_members=new Members(); - new_members.setEmail(members.getEmail()); - new_members.setName(members.getName()); - new_members.setPassword(encoded_password); - new_members.setRole(Role.USER); - new_members.setMembershipDate(new Date()); - new_members.setUsername(members.getUsername()); - memberRepository.save(new_members); - } - - public void signupAdmin(Members members) { - Optional memberOptId=memberRepository.findById(members.getMemberId()); - Optional memberOptUsername=memberRepository.findByUsername(members.getUsername()); - if(memberOptId.isPresent()) { - throw new ResourceNotFoundException("User already Exists"); - } - if(memberOptUsername.isPresent()) { - throw new ResourceNotFoundException("User already Exists"); - } - - String encoded_password=passwordEncoder.bCryptPasswordEncoder().encode(members.getPassword()); - Members new_members=new Members(); - new_members.setEmail(members.getEmail()); - new_members.setName(members.getName()); - new_members.setPassword(encoded_password); - new_members.setRole(Role.ADMIN); - new_members.setMembershipDate(new Date()); - new_members.setUsername(members.getUsername()); - memberRepository.save(new_members); - - } - public void signupLibrarian(Members members) { - Optional memberOptId=memberRepository.findById(members.getMemberId()); - Optional memberOptUsername=memberRepository.findByUsername(members.getUsername()); - if(memberOptId.isPresent()) { - throw new ResourceNotFoundException("User already Exists"); - } - if(memberOptUsername.isPresent()) { - throw new ResourceNotFoundException("User already Exists"); - } - String encoded_password=passwordEncoder.bCryptPasswordEncoder().encode(members.getPassword()); - Members new_members=new Members(); - new_members.setEmail(members.getEmail()); - new_members.setName(members.getName()); - new_members.setPassword(encoded_password); - new_members.setRole(Role.LIBRARIAN); - new_members.setMembershipDate(new Date()); - new_members.setUsername(members.getUsername()); - memberRepository.save(new_members); - } + public void signupLibrarian(Members members) { + Optional memberOptId = memberRepository.findById(members.getMemberId()); + Optional memberOptUsername = memberRepository.findByUsername(members.getUsername()); + if (memberOptId.isPresent()) { + throw new ResourceNotFoundException("User already Exists"); + } + if (memberOptUsername.isPresent()) { + throw new ResourceNotFoundException("User already Exists"); + } + String encoded_password = passwordEncoder.bCryptPasswordEncoder().encode(members.getPassword()); + Members new_members = new Members(); + new_members.setEmail(members.getEmail()); + new_members.setName(members.getName()); + new_members.setPassword(encoded_password); + new_members.setRole(Role.LIBRARIAN); + new_members.setMembershipDate(new Date()); + new_members.setUsername(members.getUsername()); + memberRepository.save(new_members); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9cd73f6..3fce839 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,3 @@ spring.application.name=libraryman-api -spring.profiles.active=${ENV:development} +spring.profiles.active=${ENV:dev} jwt.secretKey=${YOUR_JWT_SECRET_KEY} \ No newline at end of file