Skip to content
Merged
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
49 changes: 46 additions & 3 deletions src/main/java/org/example/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.example.user.repository.JpaUserRepository;
import org.example.user.repository.UserRepository;

import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Scanner;

public class App {
Expand All @@ -18,6 +20,8 @@ static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
final UserCLI userCli = new UserCLI(userService, scanner);
SearchCli searchCli = new SearchCli(scanner, EMFactory.getEntityManager());
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd");

// #######################
// Application starts here
Expand All @@ -29,24 +33,63 @@ static void main(String[] args) {
System.out.println("========================================================");

if (SessionManager.isLoggedIn()) {
System.out.println("1. Sök bok | 2. Hantera användare | 3. Logga ut | 0. Avsluta");
System.out.println("1. Sök bok | 2. Hantera användare | 3. Logga ut | 4. Mina lån | 0. Avsluta");
} else {
System.out.println("1. Sök bok | 2. Logga in/Skapa konto | 0. Avsluta");
}

System.out.print("Menyval: ");
String choice = scanner.nextLine();
LoanServices loanServices = new LoanServices(EMFactory.getEntityManager());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Move LoanServices instantiation outside the loop.

Creating a new LoanServices (and thus potentially a new EntityManager reference) on every iteration of the main loop is inefficient. This should be instantiated once before the loop, similar to searchCli and userCli.

♻️ Suggested refactor
         SearchCli searchCli = new SearchCli(scanner, EMFactory.getEntityManager());
+        LoanServices loanServices = new LoanServices(EMFactory.getEntityManager());
         DateTimeFormatter formatter =
             DateTimeFormatter.ofPattern("yyyy-MM-dd");
         
         // ...
         
             switch (choice) {
-            LoanServices loanServices = new LoanServices(EMFactory.getEntityManager());
🤖 Prompt for AI Agents
In @src/main/java/org/example/App.java at line 43, The code instantiates
LoanServices inside the main loop causing repeated creation of LoanServices and
its EntityManager; move the instantiation of LoanServices loanServices = new
LoanServices(EMFactory.getEntityManager()) out of the loop and create it once
before entering the loop alongside searchCli and userCli so the same
LoanServices/EntityManager is reused for each iteration.

switch (choice) {
case "1" -> {
searchCli.bookSearchCli();
}
case "2" -> {
if (SessionManager.isLoggedIn()) userCli.manageUserMenu();
else userCli.userMenu();
if (SessionManager.isLoggedIn()) {
userCli.manageUserMenu();
} else {
userCli.userMenu();
}
}
case "3" -> {
if (SessionManager.isLoggedIn()) userCli.logout();
}
case "4" -> {
System.out.println("Dina aktiva lån:");
List<Loan> loans = loanServices.activeLoans(SessionManager.getCurrentUser());

boolean running = true;
while (running) {
if (!loans.isEmpty()) {
int counter = 0;
for (Loan loan : loans) {
counter += 1;
System.out.println(counter + ". Titel: " + loan.getBook().getTitle() + " - Åter: " + loan.getReturnDate()
.format(formatter));
}
System.out.println("Välj nummer för den bok du vill lämna tillbaka (0 = tillbaka): ");
String inputString = scanner.nextLine();
Integer input = Integer.parseInt(inputString);
if (input >= 1 && input <= loans.size()) {
loanServices.returnBook(
SessionManager.getCurrentUser(),
loans.get(input - 1).getBook()
);
loans.remove(input - 1);
System.out.println("Du har lämnat tillbaka din bok!");
} else if (input == 0) {
running = false;
} else {
System.out.println("ogiltigt val.");
}

} else {
System.out.println("Inga aktiva lån.");
running = false;
}
}
}
Comment on lines +58 to +92

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add login check and handle NumberFormatException.

Two issues:

  1. Missing login guard: While menu option "4" is only displayed when logged in, a user can still type "4" when logged out. SessionManager.getCurrentUser() would return null, causing issues in activeLoans().

  2. Unhandled NumberFormatException: Line 73 uses Integer.parseInt() without try-catch. Non-numeric input will crash the application.

🐛 Proposed fix
                 case "4" -> {
+                    if (!SessionManager.isLoggedIn()) {
+                        System.out.println("Du måste logga in först.");
+                        break;
+                    }
                     System.out.println("Dina aktiva lån:");
                     List<Loan> loans = loanServices.activeLoans(SessionManager.getCurrentUser());

                     boolean running = true;
                     while (running) {
                         if (!loans.isEmpty()) {
                             int counter = 0;
                             for (Loan loan : loans) {
                                 counter += 1;
                                 System.out.println(counter + ". Titel: " + loan.getBook().getTitle() + " - Åter: " + loan.getReturnDate()
                                     .format(formatter));
                             }
                             System.out.println("Välj nummer för den bok du vill lämna tillbaka (0 = tillbaka): ");
                             String inputString = scanner.nextLine();
-                            Integer input = Integer.parseInt(inputString);
+                            int input;
+                            try {
+                                input = Integer.parseInt(inputString);
+                            } catch (NumberFormatException e) {
+                                System.out.println("Skriv en siffra.");
+                                continue;
+                            }
                             if (input >= 1 && input <= loans.size()) {
🤖 Prompt for AI Agents
In @src/main/java/org/example/App.java around lines 58 - 92, This code path
lacks a login guard and doesn't handle non-numeric input: check
SessionManager.getCurrentUser() for null before calling
loanServices.activeLoans() (if null, print a message asking the user to log in
and break/return to the menu), and wrap the Integer.parseInt(inputString) call
in a try-catch for NumberFormatException (on catch, print an "invalid input"
message and continue the while loop without crashing); keep using
loans.remove(...) and the existing logic when valid.

case "0" -> appRunning = false;
default -> System.out.println("Ogiltigt val.");
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/example/Book.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void setLoan(Loan loan) {
@ManyToMany(mappedBy = "books")
private Set<Genre> genres = new HashSet<>();

@OneToOne @JoinColumn(name = "loaned_book")
@OneToOne (mappedBy = "book")
private Loan loan;

public Set<Author> getAuthors() {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/example/Loan.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public Long getLoanId() {
@ManyToOne @JoinColumn (name = "loan_user")
private User user;

@OneToOne(mappedBy = "loan")
@OneToOne
@JoinColumn(name = "bookID")
private Book book;
}
40 changes: 28 additions & 12 deletions src/main/java/org/example/LoanServices.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public class LoanServices {

final private EntityManager em;
private final EntityManager em;

public LoanServices(EntityManager em) {
this.em = em;
Expand All @@ -16,7 +16,7 @@ public LoanServices(EntityManager em) {
public boolean isBookLoaned(Long bookId) {

List<Loan> loans = em.createQuery(
"SELECT l FROM Loan l WHERE l.book.bookId = :bookId AND l.returnDate IS NULL",
"SELECT l FROM Loan l WHERE l.book.bookId = :bookId AND l.returnDate IS NOT NULL",
Loan.class
)
.setParameter("bookId", bookId)
Expand All @@ -30,30 +30,35 @@ public boolean isBookLoaned(Long bookId) {
}

// Låna en bok
public boolean loanBook(Long bookId, Long userId) {
public boolean loanBook(User user, Book book) {

if (isBookLoaned(bookId)) {
if (isBookLoaned(book.getId())) {
return false;
}

User user = em.find(User.class, userId);
Book book = em.find(Book.class, bookId);
em.getTransaction().begin();

Book managedBook = em.merge(book);

Loan loan = new Loan();

loan.setUser(user);
loan.setBook(book);
loan.setLoanDate(ZonedDateTime.now());
loan.setReturnDate(null);
loan.setReturnDate(ZonedDateTime.now().plusDays((7)));

loan.setBook(managedBook);
managedBook.setLoan(loan);
em.persist(loan);
em.getTransaction().commit();

return true;
}

// Lämna tillbak en bok
// Lämna tillbaka en bok
public boolean returnBook(User user, Book book) {

Loan loan = em.createQuery(
"SELECT l FROM Loan l WHERE l.user = :user AND l.book = :book AND l.returnDate IS NULL",
"SELECT l FROM Loan l WHERE l.user = :user AND l.book = :book AND l.returnDate IS NOT NULL",
Loan.class
)
.setParameter("user", user)
Expand All @@ -68,9 +73,20 @@ public boolean returnBook(User user, Book book) {

em.getTransaction().begin();

loan.setReturnDate(ZonedDateTime.now());
book.setLoan(null);
Book managedBook = loan.getBook();
managedBook.setLoan(null);
em.remove(loan);
em.getTransaction().commit();
return true;
}

public List<Loan> activeLoans(User user) {

return em.createQuery(
"SELECT l FROM Loan l WHERE l.user = :user",
Loan.class
)
.setParameter("user", user)
.getResultList();
}
}
42 changes: 41 additions & 1 deletion src/main/java/org/example/SearchCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import org.example.user.SessionManager;

import java.util.List;
import java.util.Scanner;
Expand Down Expand Up @@ -104,13 +105,14 @@ private static void chooseAndShowBookDetails(Scanner sc, List<Book> results) {
Book selected = results.get(n - 1);
printBookDetails(selected);

System.out.print("Tryck Enter för att gå tillbaka til sök");
System.out.print("Tryck Enter för att gå tillbaka till sök");
sc.nextLine();
return;
}
}

private static void printBookDetails(Book b) {
LoanServices loanServices = new LoanServices(em);
System.out.println("\n==============================");
System.out.println(" " + b.getTitle());
System.out.println("------------------------------");
Expand All @@ -121,6 +123,44 @@ private static void printBookDetails(Book b) {
String desc = b.getDescription();
if (desc == null || desc.isBlank()) desc = "(Ingen beskrivning)";
System.out.println("\nBeskrivning:\n" + desc);

if (loanServices.isBookLoaned(b.getId())){
System.out.println("Status: Utlånad");
} else {
System.out.println("Status: Tillgänglig");

boolean isLoggedIn = SessionManager.isLoggedIn();
boolean isRunning = true;

if (isLoggedIn){
while (isRunning) {
System.out.println("1. Låna bok | 2. Tillbaka");
String choice = sc.nextLine();

switch (choice) {
case "1" -> {
loanServices.loanBook(SessionManager.getCurrentUser(), b);
isRunning = false;
System.out.println("Du har nu lånat boken!");
}

case "2" -> {
isRunning = false;
}
default -> System.out.println("Ogiltigt val, försök igen.");
}
}
}
}

// boolean isLoggedIn = SessionManager.isLoggedIn();
// User user = SessionManager.getCurrentUser();
// user.getUserId();

//todo:
// om tillgänglig och inloggad - låna bok
// om ej inloggad - skicka till logga in sidan.

System.out.println("==============================");
}

Expand Down