Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 54 additions & 28 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
Expand All @@ -14,61 +15,86 @@
<description>MyLittleLibrary-Backend</description>
<properties>
<java.version>17</java.version>
<spring-boot.version>3.3.2</spring-boot.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Spring Security OAuth2 Client -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>6.3.1</version> <!-- Match the Spring Security version -->
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>6.3.1</version>
</dependency>
<!-- Spring Security OAuth2 Resource Server -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-resource-server</artifactId>
<version>6.3.1</version> <!-- Match the Spring Security version -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<!-- MYSQL connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- Lombok (Optional, for reducing boilerplate code) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Spring Boot Starter Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.21.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Spring Boot Maven Plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
</project>

</project>
29 changes: 27 additions & 2 deletions src/main/java/com/capstone/mylittlelibrarybackend/book/Book.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.capstone.mylittlelibrarybackend.book;

import com.capstone.mylittlelibrarybackend.user.User;
import jakarta.persistence.*;

@Entity
Expand All @@ -17,7 +18,6 @@ public class Book {
generator = "book_sequence"
)
private Long id;

private String title;
private String author;
private String genre;
Expand All @@ -26,11 +26,15 @@ public class Book {
private String language;
private String image;

// Constructors, getters, and setters
@ManyToOne
@JoinColumn(name="user_id", nullable = false)
private User user;

// Default constructor
public Book() {
}

// Constructor with all fields except ID and User
public Book(String title, String author, String genre, String publishedYear, String description, String language, String image) {
this.title = title;
this.author = author;
Expand All @@ -41,6 +45,18 @@ public Book(String title, String author, String genre, String publishedYear, Str
this.image = image;
}

// Constructor with all fields including User
public Book(String title, String author, String genre, String publishedYear, String description, String language, String image, User user) {
this.title = title;
this.author = author;
this.genre = genre;
this.publishedYear = publishedYear;
this.description = description;
this.language = language;
this.image = image;
this.user = user;
}

// Getters and setters
public Long getId() {
return id;
Expand Down Expand Up @@ -106,6 +122,14 @@ public void setImage(String image) {
this.image = image;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

@Override
public String toString() {
return "Book{" +
Expand All @@ -117,6 +141,7 @@ public String toString() {
", description='" + description + '\'' +
", language='" + language + '\'' +
", image='" + image + '\'' +
", user=" + user +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.capstone.mylittlelibrarybackend.book;

import com.capstone.mylittlelibrarybackend.imageupload.UploadImage;
import com.capstone.mylittlelibrarybackend.user.User;
import com.capstone.mylittlelibrarybackend.user.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -16,35 +22,55 @@ public class BookController {

private final BookService bookService;
private final UploadImage uploadImage;
private final UserRepository userRepository;

@Autowired
public BookController(BookService bookService, UploadImage uploadImage) {
public BookController(BookService bookService, UploadImage uploadImage, UserRepository userRepository) {
this.bookService = bookService;
this.uploadImage = uploadImage;
this.userRepository = userRepository;
}

private Long getCurrentUserId() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication.getPrincipal() instanceof OAuth2User oauth2User) {
String email = (String) oauth2User.getAttributes().get("email");
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new RuntimeException("User with email " + email + " not found"));
return user.getId();
}
throw new RuntimeException("User not authenticated");
}

@GetMapping
@PreAuthorize("isAuthenticated()")
public List<Book> getBooks() {
return bookService.getBooks();
Long userId = getCurrentUserId();
return bookService.getBooks(userId);
}

@GetMapping(path = "/{bookId}")
@PreAuthorize("isAuthenticated()")
public Book getBookById(@PathVariable("bookId") Long bookId) {
return bookService.getBookById(bookId);
Long userId = getCurrentUserId();
return bookService.getBookById(bookId, userId);
}

@GetMapping(path = "/search")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<List<Book>> searchBooks(
@RequestParam(value = "title", defaultValue = "") String title,
@RequestParam(value = "author", defaultValue = "") String author,
@RequestParam(value = "genre", defaultValue = "") String genre,
@RequestParam(value = "language", defaultValue = "") String language
) {
List<Book> books = bookService.searchBooks(title, author, genre, language);
Long userId = getCurrentUserId();
List<Book> books = bookService.searchBooks(title, author, genre, language, userId);
return ResponseEntity.ok(books);
}

@PostMapping
@PreAuthorize("isAuthenticated()")
public ResponseEntity<String> addNewBook(@RequestParam("title") String title,
@RequestParam("author") String author,
@RequestParam("genre") String genre,
Expand All @@ -59,7 +85,8 @@ public ResponseEntity<String> addNewBook(@RequestParam("title") String title,
}

Book book = new Book(title, author, genre, publishedYear, description, language, imagePath);
bookService.addNewBook(book);
Long userId = getCurrentUserId();
bookService.addNewBook(book, userId);

return ResponseEntity.ok("Book added successfully");
} catch (IllegalStateException e) {
Expand All @@ -73,18 +100,19 @@ public ResponseEntity<String> addNewBook(@RequestParam("title") String title,
}
}


@PutMapping(path = "/{bookId}")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<String> updateBook(@PathVariable("bookId") Long bookId,
@RequestParam("title") String title,
@RequestParam("author") String author,
@RequestParam("genre") String genre,
@RequestParam("publishedYear") String publishedYear,
@RequestParam("description") String description,
@RequestParam("language") String language,
@RequestParam(value = "image", required = false) MultipartFile image) throws IOException {
@RequestParam(value = "image", required = false) MultipartFile image) {
try {
Book existingBook = bookService.getBookById(bookId);
Long userId = getCurrentUserId();
Book existingBook = bookService.getBookById(bookId, userId);
if (image != null && !image.isEmpty()) {
String imagePath = uploadImage.uploadImage(image);
existingBook.setImage(imagePath);
Expand All @@ -97,27 +125,26 @@ public ResponseEntity<String> updateBook(@PathVariable("bookId") Long bookId,
existingBook.setDescription(description);
existingBook.setLanguage(language);

bookService.updateBook(bookId, existingBook);
bookService.updateBook(bookId, existingBook, userId);

return ResponseEntity.ok("Book successfully updated");
} catch (IOException e) {
// Handle exceptions related to image upload
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to upload image: " + e.getMessage());
} catch (Exception e) {
// Handle other potential exceptions
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to update book: " + e.getMessage());
}
}

@DeleteMapping(path = "/{bookId}")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<String> deleteBook(@PathVariable("bookId") Long bookId) {
try {
bookService.deleteBook(bookId);
Long userId = getCurrentUserId();
bookService.deleteBook(bookId, userId);
return ResponseEntity.ok("Book successfully deleted");
} catch (Exception e) {
// Handle exceptions related to book deletion
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Failed to delete book: " + e.getMessage());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.capstone.mylittlelibrarybackend.book;

public class BookNotFoundException extends RuntimeException {

public BookNotFoundException(String message) {
super(message);
}

public BookNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {

@Query("SELECT b FROM Book b WHERE b.user.id = :userId")
List<Book> findByUserId(@Param("userId") Long userId);

@Query("SELECT b FROM Book b WHERE " +
"(:title IS NULL OR b.title LIKE %:title%) AND " +
"(:author IS NULL OR b.author LIKE %:author%) AND " +
"(:genre IS NULL OR b.genre LIKE %:genre%) AND " +
"(:language IS NULL OR b.language LIKE %:language%)")
List<Book> searchBooks(
"(:language IS NULL OR b.language LIKE %:language%) AND " +
"b.user.id = :userId")
List<Book> searchBooksByUser(
@Param("title") String title,
@Param("author") String author,
@Param("genre") String genre,
@Param("language") String language
@Param("language") String language,
@Param("userId") Long userId
);
}
Loading