diff --git a/src/main/java/com/libraryman_api/analytics/AnalyticsController.java b/src/main/java/com/libraryman_api/analytics/AnalyticsController.java index 04723bc..6786e07 100644 --- a/src/main/java/com/libraryman_api/analytics/AnalyticsController.java +++ b/src/main/java/com/libraryman_api/analytics/AnalyticsController.java @@ -2,7 +2,10 @@ import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import java.time.LocalDate; import java.util.List; diff --git a/src/main/java/com/libraryman_api/book/BookController.java b/src/main/java/com/libraryman_api/book/BookController.java index 06b48fa..a1d6545 100644 --- a/src/main/java/com/libraryman_api/book/BookController.java +++ b/src/main/java/com/libraryman_api/book/BookController.java @@ -2,7 +2,6 @@ import com.libraryman_api.exception.ResourceNotFoundException; import jakarta.validation.Valid; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -102,21 +101,22 @@ public BookDto updateBook(@PathVariable int id, @Valid @RequestBody BookDto book public void deleteBook(@PathVariable int id) { bookService.deleteBook(id); } - + /** * Searches book based on title, author, genre, etc. * It uses a keyword parameter to filter the books, and pagination is applied to the search results. * If no book is found it will return 204(No content found) http response. * If keyword is null then it will return all books. - * @param keyword the Keyword to search Book + * + * @param keyword the Keyword to search Book * @param pageable * @return */ @GetMapping("/search") - public ResponseEntity> searchBook(@RequestParam String keyword, @PageableDefault(page = 0, size = 5, sort = "title") Pageable pageable){ - Page books=bookService.searchBook(keyword,pageable); - if(!books.isEmpty()) - return ResponseEntity.ok(books); - return ResponseEntity.noContent().build(); + public ResponseEntity> searchBook(@RequestParam String keyword, @PageableDefault(page = 0, size = 5, sort = "title") Pageable pageable) { + Page books = bookService.searchBook(keyword, pageable); + if (!books.isEmpty()) + return ResponseEntity.ok(books); + return ResponseEntity.noContent().build(); } } \ No newline at end of file diff --git a/src/main/java/com/libraryman_api/book/BookDto.java b/src/main/java/com/libraryman_api/book/BookDto.java index bc8450d..a7349a1 100644 --- a/src/main/java/com/libraryman_api/book/BookDto.java +++ b/src/main/java/com/libraryman_api/book/BookDto.java @@ -7,7 +7,7 @@ public class BookDto { private int bookId; @NotBlank(message = "Title is required ") - @Size(min = 1,max = 255,message = "Title must be between 1 and 255 characters") + @Size(min = 1, max = 255, message = "Title must be between 1 and 255 characters") private String title; @NotBlank(message = "Author is required") diff --git a/src/main/java/com/libraryman_api/book/BookRepository.java b/src/main/java/com/libraryman_api/book/BookRepository.java index 96942f3..3f8f8d5 100644 --- a/src/main/java/com/libraryman_api/book/BookRepository.java +++ b/src/main/java/com/libraryman_api/book/BookRepository.java @@ -9,25 +9,25 @@ @Repository public interface BookRepository extends JpaRepository { - - /** - * This method use SQL Query for finding book based on - * title, author, genre, publishedYear, etc. By using LIKE operator - * it search from database based on keyword entered. - * - * @param keyword - * @param pageable - * @return - */ - - @Query("SELECT b FROM Book b WHERE " - + "(LOWER(b.title) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " - + "LOWER(b.author) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " - + "LOWER(b.publisher) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " - + "LOWER(b.genre) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " - + "CAST(b.publishedYear AS string) LIKE %:keyword% OR " - + "CAST(b.copiesAvailable AS string) LIKE %:keyword%)") - Page searchBook(@Param("keyword") String keyword,Pageable pageable); + + /** + * This method use SQL Query for finding book based on + * title, author, genre, publishedYear, etc. By using LIKE operator + * it search from database based on keyword entered. + * + * @param keyword + * @param pageable + * @return + */ + + @Query("SELECT b FROM Book b WHERE " + + "(LOWER(b.title) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + + "LOWER(b.author) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + + "LOWER(b.publisher) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + + "LOWER(b.genre) LIKE LOWER(CONCAT('%', :keyword, '%')) OR " + + "CAST(b.publishedYear AS string) LIKE %:keyword% OR " + + "CAST(b.copiesAvailable AS string) LIKE %:keyword%)") + Page searchBook(@Param("keyword") String keyword, Pageable pageable); } diff --git a/src/main/java/com/libraryman_api/book/BookService.java b/src/main/java/com/libraryman_api/book/BookService.java index a1b5c90..c3c5e76 100644 --- a/src/main/java/com/libraryman_api/book/BookService.java +++ b/src/main/java/com/libraryman_api/book/BookService.java @@ -181,16 +181,16 @@ public Book DtoToEntity(BookDto bookDto) { book.setIsbn(bookDto.getIsbn()); return book; } - + /** - *

This method takes String keyword and search book based on + *

This method takes String keyword and search book based on * title, author, genre, publishedYear, etc. from Book Entity.

- * + * * @param keyword * @param pageable * @return */ - public Page searchBook(String keyword,Pageable pageable ){ - return bookRepository.searchBook(keyword,pageable); + public Page searchBook(String keyword, Pageable pageable) { + return bookRepository.searchBook(keyword, pageable); } } diff --git a/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java b/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java index 3acc4ef..fad8870 100644 --- a/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java +++ b/src/main/java/com/libraryman_api/borrowing/BorrowingRepository.java @@ -11,19 +11,21 @@ @Repository public interface BorrowingRepository extends JpaRepository { -Page findByMember_memberId(int memberId, Pageable pageable); { -try { -Page borrowings = borrowingRepository.findByMember_memberId(memberId, pageable); - -if (borrowings.isEmpty()) { - throw new ResourceNotFoundException("Member didn't borrow any book"); -} -return borrowings.map(this::EntityToDto); -} catch (PropertyReferenceException ex) { - throw new InvalidSortFieldException("The specified 'sortBy' value is invalid."); -} -} - + { + try { + Page borrowings = borrowingRepository.findByMember_memberId(memberId, pageable); + + if (borrowings.isEmpty()) { + throw new ResourceNotFoundException("Member didn't borrow any book"); + } + return borrowings.map(this::EntityToDto); + } catch (PropertyReferenceException ex) { + throw new InvalidSortFieldException("The specified 'sortBy' value is invalid."); + } + } + + Page findByMember_memberId(int memberId, Pageable pageable); + @Query(value = "SELECT b.title AS bookTitle, COUNT(*) AS borrowCount " + "FROM borrowings br JOIN books b ON br.book_id = b.book_id " + "GROUP BY b.book_id ORDER BY borrowCount DESC LIMIT :limit", nativeQuery = true) diff --git a/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java b/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java index 9552800..6cd72af 100644 --- a/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java +++ b/src/main/java/com/libraryman_api/borrowing/BorrowingsDto.java @@ -21,7 +21,6 @@ public class BorrowingsDto { private Date returnDate; - public BorrowingsDto(int borrowingId, BookDto book, Fines fine, MembersDto member, Date borrowDate, Date dueDate, Date returnDate) { this.borrowingId = borrowingId; this.book = book; diff --git a/src/main/java/com/libraryman_api/email/EmailService.java b/src/main/java/com/libraryman_api/email/EmailService.java index c59e644..4bf6594 100644 --- a/src/main/java/com/libraryman_api/email/EmailService.java +++ b/src/main/java/com/libraryman_api/email/EmailService.java @@ -3,16 +3,13 @@ import com.libraryman_api.notification.NotificationRepository; import com.libraryman_api.notification.NotificationStatus; import com.libraryman_api.notification.Notifications; - 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; diff --git a/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java b/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java index bcb7ec1..3125752 100644 --- a/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/libraryman_api/exception/GlobalExceptionHandler.java @@ -10,7 +10,9 @@ import org.springframework.web.context.request.WebRequest; import java.util.Date; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Global exception handler for the LibraryMan API. This class provides @@ -75,16 +77,16 @@ public ResponseEntity invalidPasswordException(InvalidPasswordException ex, W } @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity> MethodArgumentNotValidException(MethodArgumentNotValidException ex){ + public ResponseEntity> MethodArgumentNotValidException(MethodArgumentNotValidException ex) { List allErrors = ex.getBindingResult().getAllErrors(); - HashMap map = new HashMap<>(); + HashMap map = new HashMap<>(); allErrors.forEach(objectError -> { - String message=objectError.getDefaultMessage(); - String field=((FieldError) objectError).getField(); - map.put(field,message); + String message = objectError.getDefaultMessage(); + String field = ((FieldError) objectError).getField(); + map.put(field, message); }); - return new ResponseEntity<>(map,HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(map, HttpStatus.BAD_REQUEST); } } diff --git a/src/main/java/com/libraryman_api/fine/Fines.java b/src/main/java/com/libraryman_api/fine/Fine.java similarity index 100% rename from src/main/java/com/libraryman_api/fine/Fines.java rename to src/main/java/com/libraryman_api/fine/Fine.java diff --git a/src/main/java/com/libraryman_api/member/MemberController.java b/src/main/java/com/libraryman_api/member/MemberController.java index dee2857..664c175 100644 --- a/src/main/java/com/libraryman_api/member/MemberController.java +++ b/src/main/java/com/libraryman_api/member/MemberController.java @@ -87,7 +87,7 @@ public ResponseEntity getMemberById(@PathVariable int id) { */ @PutMapping("/{id}") @PreAuthorize("hasRole('LIBRARIAN') or hasRole('ADMIN') or (hasRole('USER') and #id == authentication.principal.memberId)") - public MembersDto updateMember(@PathVariable int id,@Valid @RequestBody UpdateMembersDto membersDtoDetails) { + public MembersDto updateMember(@PathVariable int id, @Valid @RequestBody UpdateMembersDto membersDtoDetails) { return memberService.updateMember(id, membersDtoDetails); } @@ -114,7 +114,7 @@ public void deleteMember(@PathVariable int id) { @PutMapping("/{id}/password") @PreAuthorize("#id == authentication.principal.memberId") public ResponseEntity updatePassword(@PathVariable int id, - @Valid @RequestBody UpdatePasswordDto updatePasswordDto) { + @Valid @RequestBody UpdatePasswordDto updatePasswordDto) { memberService.updatePassword(id, updatePasswordDto); return ResponseEntity.ok("Password updated successfully."); } diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterService.java b/src/main/java/com/libraryman_api/newsletter/NewsletterService.java index 5c12cd1..164e59e 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterService.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterService.java @@ -3,6 +3,7 @@ import com.libraryman_api.email.EmailService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; + import java.util.Optional; import java.util.regex.Pattern; diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriber.java index e6c73a2..d267ad6 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 @@ -32,11 +33,31 @@ public NewsletterSubscriber(String email) { } // Getters and Setters - public Long getId() { return id; } - public String getEmail() { return email; } - public void setEmail(String email) { this.email = email; } - public boolean isActive() { return active; } - public void setActive(boolean active) { this.active = active; } - public String getUnsubscribeToken() { return unsubscribeToken; } - public void regenerateToken() { this.unsubscribeToken = UUID.randomUUID().toString(); } + public Long getId() { + return id; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + public String getUnsubscribeToken() { + return unsubscribeToken; + } + + public void regenerateToken() { + this.unsubscribeToken = UUID.randomUUID().toString(); + } } diff --git a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java index 2604620..3aab07e 100644 --- a/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java +++ b/src/main/java/com/libraryman_api/newsletter/NewsletterSubscriberRepository.java @@ -1,9 +1,11 @@ package com.libraryman_api.newsletter; import org.springframework.data.jpa.repository.JpaRepository; + import java.util.Optional; public interface NewsletterSubscriberRepository extends JpaRepository { Optional findByEmail(String email); + Optional findByUnsubscribeToken(String unsubscribeToken); } diff --git a/src/main/resources/application-development.properties b/src/main/resources/application-development.properties index 7175765..d529a89 100644 --- a/src/main/resources/application-development.properties +++ b/src/main/resources/application-development.properties @@ -1,37 +1,27 @@ ## Database Setup First - ## Create a Database and add database name where "Add_Your_Database_Name" is below ## Make this file as it was earlier before commiting code - # Change this connection string to this format: jdbc:mysql://{ip_address}:{port}/{database_name} spring.datasource.url=jdbc:mysql://localhost:3306/Add_Your_Database_Name - ## Add your Database Username and Password spring.datasource.username=Add_Your_UserName spring.datasource.password=Add_Your_Password - # Hibernate Dialect for MySQL 8 spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect - # Prevent early database interaction spring.jpa.properties.hibernate.boot.allow_jdbc_metadata_access=false spring.jpa.hibernate.ddl-auto=update spring.sql.init.mode=never - # Format SQL spring.jpa.properties.hibernate.format_sql=true - # Error Handling server.error.include-binding-errors=always server.error.include-message=always server.error.include-stacktrace=never server.error.include-exception=true - # Logging for Spring Security logging.level.org.springframework.security=TRACE - # --- Mail Service Setup --- - # I use docker mail service https://hub.docker.com/r/maildev/maildev # Or Web based Mail service https://mailtrap.io/ spring.mail.host=Add_Your_Mail_Service_Host @@ -41,7 +31,6 @@ spring.mail.password=Add_Your_Mail_Service_Password spring.mail.properties.mail.smtp.auth=Add_Your_Mail_Service_SMTP spring.mail.properties.mail.starttls.enable=Add_Your_Mail_Service_Start_TLS spring.mail.properties.domain_name=Add_Your_Mail_Service_Domain_Name - # --- Oauth 2.0 Configurations --- spring.security.oauth2.client.registration.google.client-name=google spring.security.oauth2.client.registration.google.client-id=ADD_YOUR_CLIENT_ID diff --git a/src/main/resources/application-production.properties b/src/main/resources/application-production.properties index 3e13ee0..2905f10 100644 --- a/src/main/resources/application-production.properties +++ b/src/main/resources/application-production.properties @@ -5,7 +5,6 @@ spring.datasource.password=${DATABASE_PASSWORD} spring.datasource.driver-class-name=${DATABASE_DRIVER_CLASS_NAME} spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=false - # --- Mail Service Setup --- spring.mail.host=${MAIL_SERVICE_HOST} spring.mail.port=${MAIL_SERVICE_PORT} @@ -14,7 +13,6 @@ spring.mail.password=${MAIL_SERVICE_PASSWORD} spring.mail.properties.mail.smtp.auth=${MAIL_SERVICE_SMTP} spring.mail.properties.mail.starttls.enable=${MAIL_SERVICE_STARTTLS} spring.mail.properties.domain_name=${MAIL_SERVICE_DOMAIN_NAME} - # --- Oauth 2.0 Configurations --- spring.security.oauth2.client.registration.google.client-name=google spring.security.oauth2.client.registration.google.client-id=${YOUR_CLIENT_ID} diff --git a/src/test/java/com/libraryman_api/LibrarymanApiApplicationTests.java b/src/test/java/com/libraryman_api/LibrarymanApiApplicationTests.java index fb0d877..e160f31 100644 --- a/src/test/java/com/libraryman_api/LibrarymanApiApplicationTests.java +++ b/src/test/java/com/libraryman_api/LibrarymanApiApplicationTests.java @@ -6,9 +6,9 @@ @SpringBootTest class LibrarymanApiApplicationTests { - @Test - void contextLoads() { + @Test + void contextLoads() { - } + } }