Skip to content

Refactored templates, controllers, and styles for improved book manag…#7

Open
JohanHiths wants to merge 2 commits intomainfrom
Service
Open

Refactored templates, controllers, and styles for improved book manag…#7
JohanHiths wants to merge 2 commits intomainfrom
Service

Conversation

@JohanHiths
Copy link
Owner

@JohanHiths JohanHiths commented Mar 9, 2026

…ement flow

  • Replaced JTE templates with Thymeleaf equivalents.
  • Updated BookController methods and added support for CRUD operations.
  • Introduced new Thymeleaf templates for creating and updating books.
  • Added global exception handling for runtime exceptions.
  • Enhanced styling with separate CSS files for forms and navigation.
  • Modified application.properties to enable hidden method filtering for DELETE/PUT requests.

Summary by CodeRabbit

  • New Features

    • Paginated book listing, create/update forms, and a user registration form
    • Reusable global navbar and new error page
  • Improvements

    • Consistent view names and improved pagination controls
    • Field-level validation on forms and clearer error display
  • Style

    • Updated/global CSS for typography, navbar, and pagination controls

…ement flow

- Replaced JTE templates with Thymeleaf equivalents.
- Updated `BookController` methods and added support for CRUD operations.
- Introduced new Thymeleaf templates for creating and updating books.
- Added global exception handling for runtime exceptions.
- Enhanced styling with separate CSS files for forms and navigation.
- Modified `application.properties` to enable hidden method filtering for DELETE/PUT requests.
@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

📝 Walkthrough

Walkthrough

Adds a global ControllerAdvice for exception handling, introduces User model and controller, refactors BookController/Service to use pagination and domain-specific BookNotFoundException, updates templates (Thymeleaf), CSS, and application property for hidden method support.

Changes

Cohort / File(s) Summary
Global Exception Handling
src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java
New @ControllerAdvice with handlers for BookNotFoundException (404), generic Exception (500), and MethodArgumentNotValidException (400); populates model and returns error view.
Book service & domain
src/main/java/org/example/lab1springboot/book/BookService.java, src/main/java/org/example/lab1springboot/BookNotFoundException.java
BookService: added pagination method getAllBooksPaginated, switched to throw BookNotFoundException when missing, updated transactional annotations and logging; new BookNotFoundException class.
Book DTOs & Validation
src/main/java/org/example/lab1springboot/book/CreateBookDTO.java
Added Lombok @Data, added title and author fields with @NotBlank validation.
Book controller & routes
src/main/java/org/example/lab1springboot/controller/BookController.java
Removed class-level /books mapping; added paginated GET /books, GET /books/create, GET /update/{id}, adjusted create/update handlers to return create-book/update-book views, added local exception handler and pagination model attributes.
Home controller
src/main/java/org/example/lab1springboot/controller/HomeController.java
Simplified model to set only activePage = "home".
User model & controller
src/main/java/org/example/lab1springboot/user/User.java, src/main/java/org/example/lab1springboot/controller/UserController.java
New User model with validation annotations and Lombok accessors; new UserController with POST /user accepting @RequestBody @Valid User.
Templates added/updated
src/main/resources/templates/books.html, .../create-book.html, .../update-book.html, .../home.html, .../navbar.html, .../error.html
Replaced books view with table + pagination and inline edit/delete; added create-book, update-book, navbar fragment and error template; updated home.html to include navbar and asset changes.
Templates removed (JTE)
src/main/jte/books.jte, src/main/jte/home.jte
Removed JTE templates previously rendering books/home.
Static assets & CSS
src/main/resources/static/css/base.css, .../navbar.css, .../books.css
Added base font-family, new navbar.css (flex navbar), and pagination styles in books.css.
Config & build
src/main/resources/application.properties, pom.xml
Enabled spring.mvc.hiddenmethod.filter.enabled=true; removed gg.jte:jte-spring-boot-starter-4 dependency and left a commented docker-compose dependency block in pom.xml.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Controller as BookController
  participant Service as BookService
  participant Repo as BookRepository
  participant View as Thymeleaf
  participant ErrorHandler as GlobalExceptionHandler

  Client->>Controller: GET /books?page=&size=
  Controller->>Service: getAllBooksPaginated(page,size)
  Service->>Repo: findAll(PageRequest)
  Repo-->>Service: Page<Book>
  Service-->>Controller: Page<Book>
  Controller->>View: render "books" (books, currentPage, totalPages)
  View-->>Client: HTML

  alt Book not found (e.g., GET /update/{id} -> missing)
    Controller->>Service: getBookById(id)
    Service->>Repo: findById(id)
    Repo-->>Service: empty
    Service-->>Controller: throw BookNotFoundException
    Controller-->>ErrorHandler: BookNotFoundException
    ErrorHandler->>View: render "error" (errorMessage)
    View-->>Client: 404 error page
  end

  alt Validation error (create/update)
    Client->>Controller: POST/PUT with invalid payload
    Controller->>ErrorHandler: MethodArgumentNotValidException
    ErrorHandler->>View: render "error" (errors map)
    View-->>Client: 400 error page
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main refactoring effort: updating templates, controllers, and styles for improved book management, which aligns with the core changes across the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Service

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/example/lab1springboot/controller/BookController.java (1)

57-79: ⚠️ Potential issue | 🔴 Critical

Edit/delete routes do not match the URLs generated by the templates.

The templates build /books/update/{id} and /books/{id}, but these handlers are registered as /update/{id} and /{id} because the controller has no /books base mapping. As-is, edit and delete actions will 404.

One consistent routing shape
 import org.springframework.validation.BindingResult;
 import org.springframework.web.bind.annotation.*;
 
-
 `@Controller`
+@RequestMapping("/books")
 public class BookController {
@@
-    `@GetMapping`("/books")
+    `@GetMapping`
     public String getBooks(`@RequestParam`(required = false) String title,Model model) {
@@
-    `@PostMapping`("books/create")
+    `@PostMapping`("/create")
     public String createBook(`@Valid` CreateBookDTO dto, BindingResult result) {
@@
-    `@GetMapping`("/update/{id}")
+    `@GetMapping`("/update/{id}")
     public String showUpdateForm(`@PathVariable` Long id, Model model) {
@@
-    `@GetMapping`("books/create")
+    `@GetMapping`("/create")
     public String showCreateForm(Model model) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 57 - 79, The controller routes for edit/delete are missing the
"/books" prefix; update the BookController routing so the template URLs
(/books/update/{id} and /books/{id}) match the handlers: add a class-level
`@RequestMapping`("/books") annotation to the BookController class (so methods
updateBook, deleteBook, and showUpdateForm keep their current
`@PutMapping/`@DeleteMapping/@GetMapping paths) or alternatively prefix each
method mapping with "/books" to ensure consistent routing.
🧹 Nitpick comments (2)
src/main/java/org/example/lab1springboot/book/CreateBookDTO.java (1)

8-15: @Data is redundant here and widens the generated API.

@Getter and @Setter already cover the accessors on this DTO. Adding @Data duplicates those methods and also generates equals/hashCode/toString, which is a bigger behavior change than this class appears to need.

Smaller change
-import lombok.Data;
 import lombok.Getter;
 
 import lombok.Setter;
 
 `@Getter`
 `@Setter`
-@Data
 public class CreateBookDTO {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/book/CreateBookDTO.java` around
lines 8 - 15, The class CreateBookDTO uses both `@Getter/`@Setter and `@Data` which
is redundant and expands the API (adding equals/hashCode/toString); remove the
`@Data` annotation and its import (lombok.Data) and keep only `@Getter` and `@Setter`
on CreateBookDTO so the DTO only generates accessors and not the additional
methods.
src/main/java/org/example/lab1springboot/controller/BookController.java (1)

37-42: Either apply the title filter or remove the parameter.

The handler accepts title, but the implementation always returns bookService.getAllBooks(). Any caller using that query parameter will silently get unfiltered results.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 37 - 42, The getBooks handler in BookController currently accepts a
title param but always returns bookService.getAllBooks(), so either apply the
title filter or remove the param: if you want filtering, modify
BookController.getBooks to call the service filtering method (e.g.,
bookService.findByTitle(title) or bookService.getBooksFilteredByTitle(title))
when title is non-empty and put that result into model.addAttribute("books",
...); if your service lacks a filter method add one (e.g., in BookService
implement findByTitle or filter the list returned by getAllBooks()), and ensure
null/empty title falls back to getAllBooks(); alternatively, remove the
`@RequestParam`(required = false) String title from the getBooks signature and any
callers expecting that query param.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/lab1springboot/controller/BookController.java`:
- Around line 75-79: The showUpdateForm method in BookController currently adds
only "book" to the model but the template expects "updateBookDTO" and "id";
change showUpdateForm (which calls bookService.getBookById) to map the retrieved
Book into an UpdateBookDTO instance, add model.addAttribute("updateBookDTO",
updateDto) and model.addAttribute("id", id) (or model.addAttribute("id",
book.getId())), so the template can bind to ${updateBookDTO} and ${id}; use the
UpdateBookDTO constructor or setters to populate fields from Book before
returning "update-book".

In `@src/main/java/org/example/lab1springboot/controller/UserController.java`:
- Around line 12-15: The CreateUser(`@RequestBody` `@Valid` User user) handler in
UserController returns the view name "user" which doesn't exist in the templates
and will fail view resolution; fix by either creating a new user.html template
matching that view name or changing the handler to return an existing view
(e.g., "home") or a redirect (e.g., "redirect:/home") after successful
creation—update the method in UserController (CreateUser) accordingly so the
returned view name matches an actual template or uses redirect to an existing
route.
- Around line 4-13: The controller is importing Tomcat's
org.apache.catalina.User instead of your application POJO, causing
deserialization to fail; update the import in UserController to use your
application's model (org.example.lab1springboot.user.User) so the `@RequestBody`
`@Valid` User user parameter refers to a concrete class, then ensure the
CreateUser method and class-level imports no longer reference
org.apache.catalina.User.

In `@src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java`:
- Around line 19-24: The handler GlobalExceptionHandler.handleRuntimeException
returns the view name "error" and adds the "errorMessage" model attribute, but
there is no corresponding error template; create a new "error" template in the
templates set that displays the errorMessage (and optionally reuses navbar or
layout fragments) so that returning "error" resolves to a user-facing page
instead of causing view-resolution failure.
- Around line 19-24: The handler method in GlobalExceptionHandler —
handleRuntimeException(RuntimeException, Model) — returns an error view but
doesn't set an HTTP status; add the
`@ResponseStatus`(HttpStatus.INTERNAL_SERVER_ERROR) annotation to that method (and
import ResponseStatus and HttpStatus) so the response returns 500 instead of 200
when a RuntimeException is handled.

In `@src/main/java/org/example/lab1springboot/user/User.java`:
- Around line 26-27: The User model currently requires a client-supplied id via
the `@NotNull` annotation on the id field which prevents normal server-side ID
generation for POST /user; remove the `@NotNull` on the id field in class User (or
better: introduce a CreateUser DTO without an id and validate that instead) and
ensure ID is assigned server-side in the controller/service that handles POST
/user (refer to User.id, the `@NotNull` annotation, and the POST /user flow when
making the change).
- Around line 13-21: The validation annotations on User fields allow
blank/whitespace values; replace `@NotNull/`@NotEmpty with `@NotBlank` on the name,
username, and email fields (i.e., the private String name, private String
username, private String email in class User) to reject empty or whitespace-only
input, and add `@Email` to the email field to enforce proper email format; keep
the message attributes (or adjust them) when updating the annotations so
validation messages remain informative.

In `@src/main/resources/templates/create-book.html`:
- Around line 10-18: The create-book template is missing the form bound to the
CreateBookDTO expected by BookController.createBook; add an HTML <form> in
create-book.html that uses Thymeleaf binding (th:object="${book}"), includes
inputs bound to the DTO properties (e.g., th:field for title, author, isbn,
etc.), and posts to the controller endpoint (the action that
BookController.createBook handles) with a submit button so the controller
receives a populated CreateBookDTO.

In `@src/main/resources/templates/home.html`:
- Around line 13-14: The template uses ${message} and ${currentDate} but
HomeController.home() only adds activePage, so those bindings will be empty; fix
by either (A) adding model attributes in HomeController.home(): put
model.addAttribute("message", "<desired text>") and
model.addAttribute("currentDate", LocalDate.now() or formatted date) so the
th:text bindings have backing data, or (B) revert the template to static content
by replacing <h1 th:text="${message}"> and <span th:text="${currentDate}"> with
plain text values; update the HomeController.home() method or the <h1>/<span>
elements accordingly.

In `@src/main/resources/templates/navbar.html`:
- Around line 6-10: The navbar stylesheet link is outside the exported fragment,
so pages that replace the node with the header fragment (th:fragment="navbar")
lose the CSS; move the <link th:href="@{/css/navbar.css}" rel="stylesheet"> into
the fragment (inside the header element defined by th:fragment="navbar") or
alternatively change the fragment to include a head fragment that injects the
stylesheet so that consumers of the header fragment (navbar) automatically get
the /css/navbar.css without needing to add it manually; update the template
around th:fragment="navbar" and the existing <link th:href...> accordingly.

In `@src/main/resources/templates/update-book.html`:
- Around line 14-17: The form in update-book.html currently has the title/author
input fields commented out so the Update Book button submits no editable data;
restore the inputs by uncommenting or re-adding the input elements that bind to
the model (the lines using th:field="*{title}" and th:field="*{author}") so the
form includes the title and author values on submit, ensuring they remain inside
the same <form> as the <button type="submit"> and keep any required attributes
(e.g., id/name or th:field) consistent with the backing model.

---

Outside diff comments:
In `@src/main/java/org/example/lab1springboot/controller/BookController.java`:
- Around line 57-79: The controller routes for edit/delete are missing the
"/books" prefix; update the BookController routing so the template URLs
(/books/update/{id} and /books/{id}) match the handlers: add a class-level
`@RequestMapping`("/books") annotation to the BookController class (so methods
updateBook, deleteBook, and showUpdateForm keep their current
`@PutMapping/`@DeleteMapping/@GetMapping paths) or alternatively prefix each
method mapping with "/books" to ensure consistent routing.

---

Nitpick comments:
In `@src/main/java/org/example/lab1springboot/book/CreateBookDTO.java`:
- Around line 8-15: The class CreateBookDTO uses both `@Getter/`@Setter and `@Data`
which is redundant and expands the API (adding equals/hashCode/toString); remove
the `@Data` annotation and its import (lombok.Data) and keep only `@Getter` and
`@Setter` on CreateBookDTO so the DTO only generates accessors and not the
additional methods.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`:
- Around line 37-42: The getBooks handler in BookController currently accepts a
title param but always returns bookService.getAllBooks(), so either apply the
title filter or remove the param: if you want filtering, modify
BookController.getBooks to call the service filtering method (e.g.,
bookService.findByTitle(title) or bookService.getBooksFilteredByTitle(title))
when title is non-empty and put that result into model.addAttribute("books",
...); if your service lacks a filter method add one (e.g., in BookService
implement findByTitle or filter the list returned by getAllBooks()), and ensure
null/empty title falls back to getAllBooks(); alternatively, remove the
`@RequestParam`(required = false) String title from the getBooks signature and any
callers expecting that query param.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eec187c8-4a73-49cf-9b77-7ccfc09b4ecc

📥 Commits

Reviewing files that changed from the base of the PR and between 86ca074 and 8642886.

📒 Files selected for processing (21)
  • src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java
  • src/main/java/org/example/lab1springboot/book/BookService.java
  • src/main/java/org/example/lab1springboot/book/CreateBookDTO.java
  • src/main/java/org/example/lab1springboot/controller/BookController.java
  • src/main/java/org/example/lab1springboot/controller/HomeController.java
  • src/main/java/org/example/lab1springboot/controller/UserController.java
  • src/main/java/org/example/lab1springboot/user/User.java
  • src/main/jte/books.jte
  • src/main/jte/home.jte
  • src/main/jte/updatebook.jte
  • src/main/resources/application.properties
  • src/main/resources/static/css/base.css
  • src/main/resources/static/css/books.css
  • src/main/resources/static/css/forms.css
  • src/main/resources/static/css/navbar.css
  • src/main/resources/static/style.css
  • src/main/resources/templates/books.html
  • src/main/resources/templates/create-book.html
  • src/main/resources/templates/home.html
  • src/main/resources/templates/navbar.html
  • src/main/resources/templates/update-book.html
💤 Files with no reviewable changes (2)
  • src/main/jte/home.jte
  • src/main/jte/books.jte

Comment on lines +75 to +79
@GetMapping("/update/{id}")
public String showUpdateForm(@PathVariable Long id, Model model) {
Book book = bookService.getBookById(id);
model.addAttribute("book", book);
return "update-book";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

showUpdateForm populates the wrong model attributes for the template.

update-book.html binds to ${updateBookDTO} and uses ${id}, but this method only exposes "book". The edit page will fail to bind correctly on the initial GET render.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 75 - 79, The showUpdateForm method in BookController currently adds
only "book" to the model but the template expects "updateBookDTO" and "id";
change showUpdateForm (which calls bookService.getBookById) to map the retrieved
Book into an UpdateBookDTO instance, add model.addAttribute("updateBookDTO",
updateDto) and model.addAttribute("id", id) (or model.addAttribute("id",
book.getId())), so the template can bind to ${updateBookDTO} and ${id}; use the
UpdateBookDTO constructor or setters to populate fields from Book before
returning "update-book".

Comment on lines +12 to +15
@PostMapping("/user")
public String CreateUser(@RequestBody @Valid User user) {

return "user";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Return an existing view or add user.html.

The success path returns "user", but the provided templates only include books.html, create-book.html, home.html, navbar.html, and update-book.html. As written, a successful POST /user still fails during view resolution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/UserController.java`
around lines 12 - 15, The CreateUser(`@RequestBody` `@Valid` User user) handler in
UserController returns the view name "user" which doesn't exist in the templates
and will fail view resolution; fix by either creating a new user.html template
matching that view name or changing the handler to return an existing view
(e.g., "home") or a redirect (e.g., "redirect:/home") after successful
creation—update the method in UserController (CreateUser) accordingly so the
returned view name matches an actual template or uses redirect to an existing
route.

Comment on lines +19 to +24
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(RuntimeException ex, Model model) {

model.addAttribute("errorMessage", ex.getMessage());

return "error";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add the missing error template or this handler will fail on first use.

This method returns "error", but the provided template set only includes books.html, create-book.html, home.html, navbar.html, and update-book.html. Without src/main/resources/templates/error.html, any handled RuntimeException turns into a view-resolution failure instead of a user-facing error page.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java` around
lines 19 - 24, The handler GlobalExceptionHandler.handleRuntimeException returns
the view name "error" and adds the "errorMessage" model attribute, but there is
no corresponding error template; create a new "error" template in the templates
set that displays the errorMessage (and optionally reuses navbar or layout
fragments) so that returning "error" resolves to a user-facing page instead of
causing view-resolution failure.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Spring MVC, when an @ExceptionHandlermethod returns a String view name and does not use@ResponseStatus, ResponseEntity, or otherwise set the response status, what HTTP status is sent?

💡 Result:

It will be HTTP 200 (OK) by default (i.e., if your @ExceptionHandler returns a String view name and you don’t set a status via @ResponseStatus, ResponseEntity, HttpServletResponse, etc., Spring leaves the response status unchanged, and the servlet container’s default is 200). [1], [2]


🏁 Script executed:

find . -name "GlobalExceptionHandler.java" -type f

Repository: JohanHiths/Lab1-Springboot

Length of output: 140


🏁 Script executed:

cat -n "./src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java"

Repository: JohanHiths/Lab1-Springboot

Length of output: 1073


Add @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) to the exception handler.

When an @ExceptionHandler method returns a view name without explicitly setting the response status, Spring defaults to HTTP 200 (OK). This causes error responses to be treated as successful responses by clients and monitoring systems, breaking HTTP semantics. The handler needs the @ResponseStatus annotation to return the correct status code.

Suggested fix
+import org.springframework.http.HttpStatus;
 import org.springframework.ui.Model;
 import org.springframework.validation.FieldError;
 import org.springframework.web.bind.MethodArgumentNotValidException;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.servlet.View;
@@
     `@ExceptionHandler`(RuntimeException.class)
+    `@ResponseStatus`(HttpStatus.INTERNAL_SERVER_ERROR)
     public String handleRuntimeException(RuntimeException ex, Model model) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ExceptionHandler(RuntimeException.class)
public String handleRuntimeException(RuntimeException ex, Model model) {
model.addAttribute("errorMessage", ex.getMessage());
return "error";
`@ExceptionHandler`(RuntimeException.class)
`@ResponseStatus`(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleRuntimeException(RuntimeException ex, Model model) {
model.addAttribute("errorMessage", ex.getMessage());
return "error";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java` around
lines 19 - 24, The handler method in GlobalExceptionHandler —
handleRuntimeException(RuntimeException, Model) — returns an error view but
doesn't set an HTTP status; add the
`@ResponseStatus`(HttpStatus.INTERNAL_SERVER_ERROR) annotation to that method (and
import ResponseStatus and HttpStatus) so the response returns 500 instead of 200
when a RuntimeException is handled.

Comment on lines +13 to +21
@NotNull(message = "Name cannot be empty")
private String name;

@NotEmpty
@NotNull(message = "Username cannot be empty")
private String username;

@NotNull(message = "Email cannot be empty")
private String email;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

These constraints still allow blank user data.

@NotNull accepts "", and @NotEmpty on username still allows whitespace-only values. If this object is used for request validation, blank name, username, and email inputs will pass despite the current messages. Use @NotBlank, and add @Email for email.

Suggested fix
+import jakarta.validation.constraints.Email;
 import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
@@
-    `@NotNull`(message = "Name cannot be empty")
+    `@NotBlank`(message = "Name cannot be empty")
     private String name;
@@
-    `@NotEmpty`
-    `@NotNull`(message = "Username cannot be empty")
+    `@NotBlank`(message = "Username cannot be empty")
     private String username;
@@
-    `@NotNull`(message = "Email cannot be empty")
+    `@NotBlank`(message = "Email cannot be empty")
+    `@Email`(message = "Email is invalid")
     private String email;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@NotNull(message = "Name cannot be empty")
private String name;
@NotEmpty
@NotNull(message = "Username cannot be empty")
private String username;
@NotNull(message = "Email cannot be empty")
private String email;
`@NotBlank`(message = "Name cannot be empty")
private String name;
`@NotBlank`(message = "Username cannot be empty")
private String username;
`@NotBlank`(message = "Email cannot be empty")
`@Email`(message = "Email is invalid")
private String email;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/user/User.java` around lines 13 -
21, The validation annotations on User fields allow blank/whitespace values;
replace `@NotNull/`@NotEmpty with `@NotBlank` on the name, username, and email
fields (i.e., the private String name, private String username, private String
email in class User) to reject empty or whitespace-only input, and add `@Email` to
the email field to enforce proper email format; keep the message attributes (or
adjust them) when updating the annotations so validation messages remain
informative.

Comment on lines +26 to +27
@NotNull(message = "Id cannot be empty")
private String id;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t require a client-supplied id on the create model.

This type is validated on POST /user, so @NotNull here forces callers to invent an identifier before creation. That usually breaks create flows or lets clients pick IDs that should be server-controlled.

Suggested direction
-    `@NotNull`(message = "Id cannot be empty")
     private String id;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@NotNull(message = "Id cannot be empty")
private String id;
private String id;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/user/User.java` around lines 26 -
27, The User model currently requires a client-supplied id via the `@NotNull`
annotation on the id field which prevents normal server-side ID generation for
POST /user; remove the `@NotNull` on the id field in class User (or better:
introduce a CreateUser DTO without an id and validate that instead) and ensure
ID is assigned server-side in the controller/service that handles POST /user
(refer to User.id, the `@NotNull` annotation, and the POST /user flow when making
the change).

Comment on lines +10 to +18
<header> <img class="logo" alt="Bookstore Logo" width="200">


<div th:replace="~{navbar :: navbar}"></div>


</header>

</body>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Create page cannot submit a book.

BookController.createBook(...) expects a bound CreateBookDTO, but this template renders only header/navigation markup. Without a form bound to ${book}, users cannot create anything.

Minimal fix sketch
 <header> <img class="logo"  alt="Bookstore Logo" width="200">
 
-
-
     <div th:replace="~{navbar :: navbar}"></div>
-
-
 </header>
+
+<main>
+    <form th:action="@{/books/create}" method="post" th:object="${book}">
+        <input type="text" th:field="*{title}" placeholder="Title">
+        <input type="text" th:field="*{author}" placeholder="Author">
+        <button type="submit">Create Book</button>
+    </form>
+</main>
 
 </body>
 </html>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<header> <img class="logo" alt="Bookstore Logo" width="200">
<div th:replace="~{navbar :: navbar}"></div>
</header>
</body>
<header> <img class="logo" alt="Bookstore Logo" width="200">
<div th:replace="~{navbar :: navbar}"></div>
</header>
<main>
<form th:action="@{/books/create}" method="post" th:object="${book}">
<input type="text" th:field="*{title}" placeholder="Title">
<input type="text" th:field="*{author}" placeholder="Author">
<button type="submit">Create Book</button>
</form>
</main>
</body>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/create-book.html` around lines 10 - 18, The
create-book template is missing the form bound to the CreateBookDTO expected by
BookController.createBook; add an HTML <form> in create-book.html that uses
Thymeleaf binding (th:object="${book}"), includes inputs bound to the DTO
properties (e.g., th:field for title, author, isbn, etc.), and posts to the
controller endpoint (the action that BookController.createBook handles) with a
submit button so the controller receives a populated CreateBookDTO.

Comment on lines +13 to +14
<h1 th:text="${message}">Welcome Message</h1>
<p>Today's date is: <span th:text="${currentDate}">Date</span></p>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

These bindings no longer have backing model attributes.

HomeController.home() only adds activePage, so ${message} and ${currentDate} will render empty here. Either repopulate them in the controller or switch this section back to static content.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/home.html` around lines 13 - 14, The template
uses ${message} and ${currentDate} but HomeController.home() only adds
activePage, so those bindings will be empty; fix by either (A) adding model
attributes in HomeController.home(): put model.addAttribute("message", "<desired
text>") and model.addAttribute("currentDate", LocalDate.now() or formatted date)
so the th:text bindings have backing data, or (B) revert the template to static
content by replacing <h1 th:text="${message}"> and <span
th:text="${currentDate}"> with plain text values; update the
HomeController.home() method or the <h1>/<span> elements accordingly.

Comment on lines +6 to +10
<link th:href="@{/css/navbar.css}" rel="stylesheet">
</head>
<body>

<header class="navbar" th:fragment="navbar">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Navbar CSS is not carried with the fragment.

Consumers replace a node with the header fragment from Line 10, but the stylesheet link at Line 6 stays in this standalone template. Pages that include ~{navbar :: navbar} will therefore render an unstyled navbar unless they also import /css/navbar.css themselves.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/navbar.html` around lines 6 - 10, The navbar
stylesheet link is outside the exported fragment, so pages that replace the node
with the header fragment (th:fragment="navbar") lose the CSS; move the <link
th:href="@{/css/navbar.css}" rel="stylesheet"> into the fragment (inside the
header element defined by th:fragment="navbar") or alternatively change the
fragment to include a head fragment that injects the stylesheet so that
consumers of the header fragment (navbar) automatically get the /css/navbar.css
without needing to add it manually; update the template around
th:fragment="navbar" and the existing <link th:href...> accordingly.

Comment on lines +14 to +17
<!-- <input type="text" th:field="*{title}"-->
<!-- <input type="text" th:field="*{author}"-->

<button type="submit">Update Book</button>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

The update form submits no book fields.

With the inputs commented out, clicking Update Book sends no editable data, so the request cannot change the title/author values.

Minimal fix sketch
-<!--       <input type="text" th:field="*{title}"-->
-<!--        <input type="text" th:field="*{author}"-->
+        <input type="text" th:field="*{title}">
+        <input type="text" th:field="*{author}">
 
         <button type="submit">Update Book</button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<!-- <input type="text" th:field="*{title}"-->
<!-- <input type="text" th:field="*{author}"-->
<button type="submit">Update Book</button>
<input type="text" th:field="*{title}">
<input type="text" th:field="*{author}">
<button type="submit">Update Book</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/update-book.html` around lines 14 - 17, The form
in update-book.html currently has the title/author input fields commented out so
the Update Book button submits no editable data; restore the inputs by
uncommenting or re-adding the input elements that bind to the model (the lines
using th:field="*{title}" and th:field="*{author}") so the form includes the
title and author values on submit, ensuring they remain inside the same <form>
as the <button type="submit"> and keep any required attributes (e.g., id/name or
th:field) consistent with the backing model.

- Added pagination support to `BookController` and updated templates with pagination controls.
- Introduced `BookNotFoundException` for improved error handling.
- Enhanced `GlobalExceptionHandler` to handle various exceptions including validation and runtime errors.
- Updated styles and templates to improve user experience for error and paging scenarios.
- Refactored `BookService` for cleaner logic and added transactional annotations.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

♻️ Duplicate comments (1)
src/main/java/org/example/lab1springboot/controller/BookController.java (1)

77-83: ⚠️ Potential issue | 🟠 Major

Populate the DTO before rendering the edit form.

The book is loaded on Line 77, but dto stays empty. The initial render will lose the current title/author values.

Suggested fix
     public String showUpdateForm(`@PathVariable` Long id, Model model) {
         Book book = bookService.getBookById(id);

         UpdateBookDTO dto = new UpdateBookDTO();
+        dto.setTitle(book.getTitle());
+        dto.setName(book.getAuthor());

         model.addAttribute("updateBookDTO", dto);
         model.addAttribute("id", id);
         return "update-book";
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 77 - 83, The UpdateBookDTO is created empty after loading the book
in BookController (bookService.getBookById(id)) so the edit form shows no
current values; populate the DTO fields from the loaded Book (e.g., title,
author, etc.) before calling model.addAttribute("updateBookDTO", dto) and keep
adding the id as before so the form is pre-filled with the book's existing data.
🧹 Nitpick comments (1)
src/main/java/org/example/lab1springboot/controller/BookController.java (1)

94-99: Remove the duplicate BookNotFoundException handler.

src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java:26-33 already returns the same 404/error view. Keeping both copies makes future changes easy to drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 94 - 99, Remove the duplicate BookNotFoundException handler method
from BookController: delete the handleBookNotFound(BookNotFoundException ex,
Model model) method so the application uses the centralized
GlobalExceptionHandler (which already handles BookNotFoundException and returns
the 404/error view). After removal, clean up any now-unused imports in
BookController and run tests to ensure no references remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/lab1springboot/controller/BookController.java`:
- Around line 57-58: The route annotations in BookController (e.g., updateBook)
don't match the URLs used by the templates (/books/update/{id} and posts to
/books/{id}), causing 404s; update the mapping annotations so they are prefixed
with /books (for example change `@PutMapping`("/{id}") to
`@PutMapping`("/books/{id}") and any `@GetMapping` for the edit form to
`@GetMapping`("/books/update/{id}")), and likewise adjust the delete handler
mapping (e.g., deleteBook) to `@PostMapping` or `@DeleteMapping`("/books/{id}") as
the template expects; update the annotations on the methods named updateBook,
deleteBook (and the edit form handler, if present) so the controller paths and
templates are consistent.
- Around line 101-107: Validate the paging parameters in the controller before
calling bookService.getAllBooksPaginated: in getAllBooks check that page >= 0
and size >= 1 and if not throw a client error (e.g., throw new
ResponseStatusException(HttpStatus.BAD_REQUEST, "page must be >= 0 and size must
be >= 1")) so a bad query string yields HTTP 400 instead of allowing
PageRequest.of(page, size) (or any similar call inside
bookService.getAllBooksPaginated) to throw a 500.

In `@src/main/resources/templates/error.html`:
- Line 7: The template contains a malformed HTML tag '<<body>' which breaks
markup; open the template and replace the incorrect '<<body>' token with a
proper '<body>' start tag and ensure there is a matching '</body>' closing tag
(verify surrounding template logic around the body element) so the document
structure is valid.
- Around line 10-11: The template hardcodes "404" causing wrong status display;
change the <h1> to render a status placeholder (e.g., use the template engine's
expression like ${status}) instead of the literal "404", and ensure
GlobalExceptionHandler populates that model attribute when returning the error
view (update the methods in GlobalExceptionHandler to add a "status" attribute
with the actual HTTP status code before returning the template).
- Around line 14-16: The template only displays errorMessage and thus ignores
validation details placed by
GlobalExceptionHandler.handleMethodArgumentNotValidException under the "errors"
model attribute; update the error.html to also render the "errors" collection
(e.g., check if errors exists and iterate to show each field/message) in
addition to the existing errorMessage so validation failures surface the actual
field-level messages.

---

Duplicate comments:
In `@src/main/java/org/example/lab1springboot/controller/BookController.java`:
- Around line 77-83: The UpdateBookDTO is created empty after loading the book
in BookController (bookService.getBookById(id)) so the edit form shows no
current values; populate the DTO fields from the loaded Book (e.g., title,
author, etc.) before calling model.addAttribute("updateBookDTO", dto) and keep
adding the id as before so the form is pre-filled with the book's existing data.

---

Nitpick comments:
In `@src/main/java/org/example/lab1springboot/controller/BookController.java`:
- Around line 94-99: Remove the duplicate BookNotFoundException handler method
from BookController: delete the handleBookNotFound(BookNotFoundException ex,
Model model) method so the application uses the centralized
GlobalExceptionHandler (which already handles BookNotFoundException and returns
the 404/error view). After removal, clean up any now-unused imports in
BookController and run tests to ensure no references remain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2a403413-5ce7-4623-acf6-f31f4f5251be

📥 Commits

Reviewing files that changed from the base of the PR and between 8642886 and e49ad59.

📒 Files selected for processing (10)
  • pom.xml
  • src/main/java/org/example/lab1springboot/BookNotFoundException.java
  • src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java
  • src/main/java/org/example/lab1springboot/book/BookService.java
  • src/main/java/org/example/lab1springboot/controller/BookController.java
  • src/main/java/org/example/lab1springboot/controller/UserController.java
  • src/main/resources/application.properties
  • src/main/resources/static/css/books.css
  • src/main/resources/templates/books.html
  • src/main/resources/templates/error.html
✅ Files skipped from review due to trivial changes (2)
  • src/main/resources/static/css/books.css
  • pom.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/example/lab1springboot/controller/UserController.java

Comment on lines 57 to 58
@PutMapping("/{id}")
public String updateBook(@PathVariable Long id, @Valid UpdateBookDTO updateBookDTO, BindingResult bindingResult, Model model) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Prefix the edit and delete endpoints consistently.

src/main/resources/templates/books.html links to /books/update/{id} and posts to /books/{id}, but these handlers are registered at /update/{id} and /{id}. Edit and delete will 404 until the mappings match.

Suggested fix
-    `@PutMapping`("/{id}")
+    `@PutMapping`("/books/{id}")
     public String updateBook(`@PathVariable` Long id, `@Valid` UpdateBookDTO updateBookDTO, BindingResult bindingResult, Model model) {
@@
-    `@DeleteMapping`("/{id}")
+    `@DeleteMapping`("/books/{id}")
     public String deleteBook(`@PathVariable` Long id) {
@@
-    `@GetMapping`("/update/{id}")
+    `@GetMapping`("/books/update/{id}")
     public String showUpdateForm(`@PathVariable` Long id, Model model) {

Also applies to: 68-75

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 57 - 58, The route annotations in BookController (e.g., updateBook)
don't match the URLs used by the templates (/books/update/{id} and posts to
/books/{id}), causing 404s; update the mapping annotations so they are prefixed
with /books (for example change `@PutMapping`("/{id}") to
`@PutMapping`("/books/{id}") and any `@GetMapping` for the edit form to
`@GetMapping`("/books/update/{id}")), and likewise adjust the delete handler
mapping (e.g., deleteBook) to `@PostMapping` or `@DeleteMapping`("/books/{id}") as
the template expects; update the annotations on the methods named updateBook,
deleteBook (and the edit form handler, if present) so the controller paths and
templates are consistent.

Comment on lines +101 to +107
@GetMapping("/books")
public String getAllBooks(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size,
Model model) {

Page<Book> bookPage = bookService.getAllBooksPaginated(page, size);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reject invalid paging parameters up front.

PageRequest.of(page, size) will throw for page < 0 or size < 1, so a bad query string currently turns into a 500 instead of a client error.

Suggested fix
+import org.springframework.web.server.ResponseStatusException;
@@
     public String getAllBooks(
             `@RequestParam`(defaultValue = "0") int page,
             `@RequestParam`(defaultValue = "5") int size,
             Model model) {
+        if (page < 0 || size < 1) {
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid pagination parameters");
+        }

         Page<Book> bookPage = bookService.getAllBooksPaginated(page, size);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/lab1springboot/controller/BookController.java`
around lines 101 - 107, Validate the paging parameters in the controller before
calling bookService.getAllBooksPaginated: in getAllBooks check that page >= 0
and size >= 1 and if not throw a client error (e.g., throw new
ResponseStatusException(HttpStatus.BAD_REQUEST, "page must be >= 0 and size must
be >= 1")) so a bad query string yields HTTP 400 instead of allowing
PageRequest.of(page, size) (or any similar call inside
bookService.getAllBooksPaginated) to throw a 500.

<meta charset="UTF-8">
<title>Title</title>
</head>
<<body>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix the malformed <body> tag.

<<body> leaves the template with invalid markup and can break layout/styling unpredictably.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/error.html` at line 7, The template contains a
malformed HTML tag '<<body>' which breaks markup; open the template and replace
the incorrect '<<body>' token with a proper '<body>' start tag and ensure there
is a matching '</body>' closing tag (verify surrounding template logic around
the body element) so the document structure is valid.

Comment on lines +10 to +11
<h1>404</h1>
<h2>Woopsie daisy! Page went to the moon.</h2>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't hardcode 404 in this shared error page.

src/main/java/org/example/lab1springboot/GlobalExceptionHandler.java also returns this template for 400 and 500, so those paths will incorrectly render as “404”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/error.html` around lines 10 - 11, The template
hardcodes "404" causing wrong status display; change the <h1> to render a status
placeholder (e.g., use the template engine's expression like ${status}) instead
of the literal "404", and ensure GlobalExceptionHandler populates that model
attribute when returning the error view (update the methods in
GlobalExceptionHandler to add a "status" attribute with the actual HTTP status
code before returning the template).

Comment on lines +14 to +16
<p th:text="${errorMessage}">We cannot find the page you were looking for</p>

<a th:href="@{/}" style="text-decoration: none; color: white; background: #6d15b5; padding: 10px 20px; border-radius: 5px;">Back to Earth (Home)</a>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Render validation errors here as well.

GlobalExceptionHandler.handleMethodArgumentNotValidException(...) stores field errors under errors, but this template only reads errorMessage. Validation failures will therefore lose the actual reason.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/error.html` around lines 14 - 16, The template
only displays errorMessage and thus ignores validation details placed by
GlobalExceptionHandler.handleMethodArgumentNotValidException under the "errors"
model attribute; update the error.html to also render the "errors" collection
(e.g., check if errors exists and iterate to show each field/message) in
addition to the existing errorMessage so validation failures surface the actual
field-level messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant