Conversation
…points in Postman
…just Thymeleaf forms
WalkthroughA basic Java template project is transformed into a Spring Boot 3.5.6 application featuring MySQL persistence, Spring Security authentication with in-memory users, REST and MVC controllers for user and workout management, a complete service/repository layer pattern, and HTML templates with Thymeleaf. Build configuration is migrated to Spring Boot conventions with simplified Maven plugins. Legacy test files are replaced with Spring Boot test classes. Changes
Sequence Diagram(s)sequenceDiagram
actor User as User/Browser
participant MVC as MVC Controller<br/>(UserViewController)
participant Service as Service<br/>(UserService)
participant Repo as Repository<br/>(UserRepository)
participant DB as MySQL DB
User->>MVC: GET /users
MVC->>Service: getAllUsers()
Service->>Repo: findAll()
Repo->>DB: SELECT * FROM users
DB-->>Repo: User records
Repo-->>Service: List<User>
Service-->>MVC: List<User>
MVC-->>User: Render users.html with table
User->>MVC: POST /users/delete/{id}
MVC->>Service: deleteUser(id)
Service->>Repo: deleteById(id)
Repo->>DB: DELETE FROM users
DB-->>Repo: Success
Repo-->>Service: void
Service-->>MVC: void
MVC-->>User: Redirect to /users
sequenceDiagram
actor Client as REST Client
participant Sec as Security Filter<br/>(HTTP Basic)
participant API as REST Controller<br/>(WorkoutController)
participant Service as Service<br/>(WorkoutService)
participant Repo as Repository<br/>(WorkoutRepository)
participant DB as MySQL DB
Client->>Sec: GET /api/workouts<br/>(with credentials)
Sec->>Sec: Authenticate via<br/>InMemoryUserDetailsManager
alt Authenticated
Sec->>API: Proceed
API->>Service: getAllWorkouts()
Service->>Repo: findAll()
Repo->>DB: SELECT * FROM workouts
DB-->>Repo: Workout records
Repo-->>Service: List<Workout>
Service-->>API: List<Workout>
API-->>Client: HTTP 200 + JSON array
else Unauthenticated
Sec-->>Client: HTTP 401 Unauthorized
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (18)
src/main/java/org/example/model/User.java (2)
15-17: Consider adding validation and unique constraints.The entity fields lack validation annotations and unique constraints. Consider adding:
@Column(unique = true, nullable = false)for username and email@NotBlankor@NotNullvalidation annotationsThis would prevent duplicate usernames/emails and ensure data integrity at both the database and application levels.
Example:
@Column(unique = true, nullable = false) @NotBlank private String username; @Column(unique = true, nullable = false) @Email @NotBlank private String email; @Column(nullable = false) @NotBlank private String password;
8-54: Consider implementing equals(), hashCode(), and toString().JPA entities benefit from properly implemented
equals(),hashCode(), andtoString()methods. This ensures correct behavior when using entities in collections, comparing instances, and debugging. Consider using theidfield for equals/hashCode or IDE-generated implementations.pom.xml (2)
55-59: Remove explicit version for mysql-connector-j.The Spring Boot parent POM manages dependency versions, including
mysql-connector-j. Explicitly specifying the version can lead to conflicts and prevents automatic updates through Spring Boot version upgrades.Apply this diff:
<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> - <version>9.5.0</version> </dependency>
16-28: Remove or populate empty metadata tags.The POM contains empty tags for
<url/>,<license/>,<developer/>, and<scm/>. Either populate these with actual information or remove them to keep the POM clean.Apply this diff to remove them:
- <url/> - <licenses> - <license/> - </licenses> - <developers> - <developer/> - </developers> - <scm> - <connection/> - <developerConnection/> - <tag/> - <url/> - </scm>src/main/resources/static/css/style.css (1)
2-57: LGTM! CSS provides clean and functional styling.The styles create a responsive, visually appealing interface with good hover effects and spacing. The implementation is appropriate for this project.
Optional: Consider using CSS custom properties (variables) for repeated color values to improve maintainability:
:root { --primary-color: #aa69f5; --hover-color: #d6eaf8; --background-color: #f5f6fa; --text-color: #2c3e50; }src/main/resources/templates/users.html (1)
11-25: Align table headers with columns and consider CSRF tokenYou render three data columns (username, email, delete button) but only define two headers; adding an “Actions” header will keep the table aligned and clearer. Also, if CSRF protection is enabled in Spring Security, you should include the CSRF token in this POST delete form to avoid 403 errors.
Example diff:
- <tr> - <th>Username</th> - <th>Email</th> - </tr> + <tr> + <th>Username</th> + <th>Email</th> + <th>Actions</th> + </tr> @@ - <td> - <form th:action="@{/users/delete/{id}(id=${user.id})}" method="post"> - <button type="submit" class="button" onclick="return confirm('Are you sure?')">Delete</button> - </form> - </td> + <td> + <form th:action="@{/users/delete/{id}(id=${user.id})}" method="post"> + <!-- Uncomment/adapt if CSRF is enabled --> + <!-- <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> --> + <button type="submit" class="button" onclick="return confirm('Are you sure?')">Delete</button> + </form> + </td>src/main/resources/application.properties (1)
2-9: Treat these datasource and JPA settings as dev-only defaultsFor this exercise repo, hard-coded DB creds and
ddl-auto=update/show-sql=trueare fine, but in a more realistic setup you’d typically:
- Move
spring.datasource.username/passwordto external config (env vars, profiles).- Restrict
spring.jpa.hibernate.ddl-auto=updateandspring.jpa.show-sql=trueto a local/dev profile.- Optionally rely on Spring Boot’s auto-detected driver and dialect instead of configuring
driver-class-nameandhibernate.dialectexplicitly.This isn’t blocking here, but worth keeping in mind if the app is ever promoted beyond training use. Based on learnings
src/main/java/org/example/service/workout/WorkoutService.java (1)
7-12: Clarify service contract and consider using a common language for commentsThe interface is straightforward, but two small improvements could help:
- Document what happens when
getWorkoutById(Long id)is called with a non‑existing id (exception vsnull, etc.), so callers know what to expect.- Consider rewriting the Swedish comment to English (or the team’s agreed language) to keep the codebase consistent for all contributors.
src/test/java/org/example/security/SecurityAccessTest.java (1)
20-31: Consider expanding security test coverage to include User endpoints.The security tests for
/api/workoutsare well-structured and cover both authenticated and unauthenticated scenarios. However, the/api/usersendpoint (introduced in UserController) also requires security validation.Consider adding similar tests for
/api/users:+ @Test + void adminShouldAccessUsersPage() throws Exception { + mockMvc.perform(get("/api/users") + .with(httpBasic("admin", "adminpass"))) + .andExpect(status().isOk()); + } + + @Test + public void testGetAllUsers_unauthenticated_shouldReturn401() throws Exception { + mockMvc.perform(get("/api/users")) + .andExpect(status().isUnauthorized()); + }src/main/java/org/example/service/user/UserService.java (1)
7-13: Interface looks good.The service interface follows standard Spring conventions with appropriate CRUD method signatures. The Swedish comment is understandable given the learning context, though maintaining consistent language (English) throughout the codebase can improve maintainability.
src/main/java/org/example/controller/UserViewController.java (1)
22-32: Consider adding a POST endpoint for creating users.The controller handles listing and deleting users, but lacks the ability to add new users via the UI. WorkoutViewController includes a
/addendpoint for creating workouts. Adding a similar endpoint here would provide consistent functionality across both entities.Consider adding:
@PostMapping("/add") public String addUser(@ModelAttribute User user) { userService.createUser(user); return "redirect:/users"; }This would align with the pattern established in WorkoutViewController and require a corresponding form in the users.html template.
src/main/java/org/example/controller/UserController.java (1)
18-36: Consider enhancing error handling and HTTP status codes.The REST endpoints implement basic CRUD operations correctly. For a more robust API, consider these improvements:
- getUserById (Line 29-31): Returns null when user not found. Consider returning
ResponseEntity<User>with a 404 status for missing users.- createUser (Line 24-26): Could return
201 Createdstatus and validate input.- deleteUser (Line 34-36): Returns void. Consider returning
204 No Contentor handling non-existent IDs with a 404.Example for getUserById:
@GetMapping("/{id}") public ResponseEntity<User> getUserById(@PathVariable Long id) { User user = userService.getUserById(id); return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build(); }src/main/java/org/example/controller/WorkoutController.java (1)
21-42: Consider enhancing error handling and maintaining language consistency.Similar to UserController, the REST endpoints would benefit from proper error handling:
- getWorkoutById (Line 35-37): Returns null when workout not found. Consider using
ResponseEntity<Workout>with 404 status.- createWorkout (Line 29-31): Could return
201 Createdstatus.- deleteWorkout (Line 40-42): Returns void. Consider returning
204 No Contentor handling non-existent IDs.Additionally, the Swedish comments (lines 21, 27, 33) are understandable in a learning context, but maintaining consistent language throughout the codebase improves maintainability.
src/main/java/org/example/service/user/UserServiceImpl.java (1)
27-30: Consider returning Optional instead of null.Line 29 uses
orElse(null), which forces callers to perform null checks. ReturningOptional<User>would provide a more expressive API and align with modern Java practices.@Override -public User getUserById(Long id) { - return userRepository.findById(id).orElse(null); +public Optional<User> getUserById(Long id) { + return userRepository.findById(id); }This would require updating the service interface and controller accordingly.
src/test/java/org/example/controller/WorkoutControllerTest.java (1)
28-97: Excellent test coverage for WorkoutController.The test class demonstrates good practices:
- Proper use of
@WebMvcTestto test only the controller layer- Mock bean configuration through
@TestConfiguration- Security testing for both authenticated and unauthenticated scenarios
- CSRF token handling for state-changing operations
- JSON response validation with appropriate date serialization
Consider adding similar test coverage for
UserControllerto ensure consistent testing across all REST endpoints.src/main/java/org/example/service/workout/WorkoutServiceImpl.java (1)
34-37: Consider returning Optional instead of null.Returning
nullviaorElse(null)can lead to NPE in calling code. Consider returningOptional<Workout>from the service method to make the absence of a value explicit.If you'd like to refactor:
@Override -public Workout getWorkoutById(Long id) { - return workoutRepository.findById(id).orElse(null); +public Optional<Workout> getWorkoutById(Long id) { + return workoutRepository.findById(id); }Note: This would require updating the service interface and any calling code.
src/main/java/org/example/model/Workout.java (2)
10-19: Consider adding validation constraints.The entity fields lack validation annotations. For a more robust implementation, consider adding constraints like
@NotBlankfor name and@NotNullor@Minfor numeric fields to ensure data integrity.Example:
@Entity public class Workout { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank(message = "Name is required") private String name; @NotNull(message = "Duration is required") @Min(value = 1, message = "Duration must be positive") private Integer duration; private Integer caloriesBurned; @NotNull(message = "Date is required") private LocalDate date; // ... }
33-67: Consider adding equals, hashCode, and toString methods.For JPA entities, it's good practice to override
equals(),hashCode(), andtoString()methods. This helps with entity comparison, collection usage, and debugging.Example (focusing on business key):
@Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Workout)) return false; Workout workout = (Workout) o; return Objects.equals(id, workout.id); } @Override public int hashCode() { return Objects.hash(id); } @Override public String toString() { return "Workout{" + "id=" + id + ", name='" + name + '\'' + ", duration=" + duration + ", caloriesBurned=" + caloriesBurned + ", date=" + date + '}'; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (26)
docker-compose.yml(1 hunks)pom.xml(1 hunks)src/main/java/org/example/App.java(0 hunks)src/main/java/org/example/Exercise2025Application.java(1 hunks)src/main/java/org/example/config/SecurityConfig.java(1 hunks)src/main/java/org/example/controller/UserController.java(1 hunks)src/main/java/org/example/controller/UserViewController.java(1 hunks)src/main/java/org/example/controller/WorkoutController.java(1 hunks)src/main/java/org/example/controller/WorkoutViewController.java(1 hunks)src/main/java/org/example/model/User.java(1 hunks)src/main/java/org/example/model/Workout.java(1 hunks)src/main/java/org/example/repository/UserRepository.java(1 hunks)src/main/java/org/example/repository/WorkoutRepository.java(1 hunks)src/main/java/org/example/service/user/UserService.java(1 hunks)src/main/java/org/example/service/user/UserServiceImpl.java(1 hunks)src/main/java/org/example/service/workout/WorkoutService.java(1 hunks)src/main/java/org/example/service/workout/WorkoutServiceImpl.java(1 hunks)src/main/resources/application.properties(1 hunks)src/main/resources/static/css/style.css(1 hunks)src/main/resources/templates/users.html(1 hunks)src/main/resources/templates/workouts.html(1 hunks)src/test/java/org/example/AppIT.java(0 hunks)src/test/java/org/example/AppTest.java(0 hunks)src/test/java/org/example/Exercise2025ApplicationTest.java(1 hunks)src/test/java/org/example/controller/WorkoutControllerTest.java(1 hunks)src/test/java/org/example/security/SecurityAccessTest.java(1 hunks)
💤 Files with no reviewable changes (3)
- src/test/java/org/example/AppIT.java
- src/test/java/org/example/AppTest.java
- src/main/java/org/example/App.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: jennymakki
Repo: fungover/exercise2025 PR: 204
File: src/main/java/org/example/config/SecurityConfig.java:19-24
Timestamp: 2025-11-16T13:56:11.263Z
Learning: The repository fungover/exercise2025 is an exercise/learning project where hard-coded credentials and other shortcuts for ease of testing are acceptable and intentional.
📚 Learning: 2025-09-10T14:02:54.365Z
Learnt from: alfredbrannare
Repo: fungover/exercise2025 PR: 66
File: src/test/java/org/example/service/WarehouseTest.java:1-21
Timestamp: 2025-09-10T14:02:54.365Z
Learning: In the fungover/exercise2025 project, static mocking of LocalDateTime.now() works successfully with the existing Mockito setup without requiring mockito-inline dependency.
Applied to files:
src/test/java/org/example/Exercise2025ApplicationTest.java
📚 Learning: 2025-11-16T13:56:11.263Z
Learnt from: jennymakki
Repo: fungover/exercise2025 PR: 204
File: src/main/java/org/example/config/SecurityConfig.java:19-24
Timestamp: 2025-11-16T13:56:11.263Z
Learning: The repository fungover/exercise2025 is an exercise/learning project where hard-coded credentials and other shortcuts for ease of testing are acceptable and intentional.
Applied to files:
src/test/java/org/example/Exercise2025ApplicationTest.javasrc/main/resources/application.propertiessrc/main/java/org/example/Exercise2025Application.javapom.xml
🧬 Code graph analysis (6)
src/main/java/org/example/service/user/UserServiceImpl.java (1)
src/main/java/org/example/service/workout/WorkoutServiceImpl.java (1)
Service(10-44)
src/main/java/org/example/controller/UserViewController.java (1)
src/main/java/org/example/controller/WorkoutViewController.java (1)
Controller(9-36)
src/main/java/org/example/controller/UserController.java (1)
src/main/java/org/example/controller/WorkoutController.java (1)
RestController(10-43)
src/main/java/org/example/service/workout/WorkoutServiceImpl.java (1)
src/main/java/org/example/service/user/UserServiceImpl.java (1)
Service(9-36)
src/main/java/org/example/controller/WorkoutViewController.java (1)
src/main/java/org/example/controller/UserViewController.java (1)
Controller(12-33)
src/main/java/org/example/controller/WorkoutController.java (1)
src/main/java/org/example/controller/UserController.java (1)
RestController(9-37)
🔇 Additional comments (12)
src/main/java/org/example/config/SecurityConfig.java (2)
19-30: LGTM! Security configuration is appropriate for this exercise project.The security filter chain correctly enforces authentication on all requests, enables form login, and configures HTTP Basic authentication. The configuration is well-structured and follows Spring Security best practices.
34-48: LGTM! In-memory users are appropriate for this exercise project.The use of
withDefaultPasswordEncoder()and hard-coded credentials is acceptable for this learning/exercise project context.Based on learnings.
docker-compose.yml (1)
1-10: LGTM! Docker Compose configuration is appropriate for development.The MySQL service configuration is well-structured with appropriate environment variables and port mapping. The hard-coded credentials are acceptable for this exercise/development environment.
Based on learnings.
src/main/java/org/example/repository/WorkoutRepository.java (1)
1-7: LGTM! Standard Spring Data JPA repository.The repository interface follows Spring Data JPA conventions correctly and will provide all standard CRUD operations for the Workout entity.
src/main/resources/templates/workouts.html (1)
1-40: LGTM! Template structure and Thymeleaf integration look good.The template properly uses Thymeleaf syntax for rendering workouts, includes proper CSRF protection (automatically handled by Spring Security with Thymeleaf), and provides good user experience with confirmation dialogs for deletion.
pom.xml (1)
30-30: Java 25 is released and generally available. Java 25 reached General Availability on September 16, 2025, and production-ready builds are available from Microsoft and other vendors. The specified version is appropriate for development and CI/CD environments.src/main/java/org/example/repository/UserRepository.java (1)
1-7: Repository definition looks goodThe
UserRepositorycorrectly extends the Spring DataJpaRepositorywithUserandLongand keeps the interface minimal. No changes needed here.src/test/java/org/example/Exercise2025ApplicationTest.java (1)
6-11: Good use of a context load smoke testThe
@SpringBootTest+contextLoads()pattern is a solid way to verify the full application context starts with the new Spring Boot setup. Looks fine as-is.src/main/java/org/example/controller/WorkoutViewController.java (1)
19-35: LGTM!The controller provides a complete set of operations for managing workouts through the UI. Constructor injection, proper delegation to the service layer, and consistent redirect patterns are all well-implemented.
src/main/java/org/example/service/workout/WorkoutServiceImpl.java (2)
10-17: LGTM!Constructor injection and service setup follow Spring best practices.
20-23: LGTM!Simple delegation pattern is appropriate here.
src/main/java/org/example/model/Workout.java (1)
21-31: LGTM!The constructors are correctly designed for JPA: a no-arg constructor for the framework and a parameterized constructor excluding the auto-generated ID.
| void main(String[] args) { | ||
| SpringApplication.run(Exercise2025Application.class, args); | ||
| } |
There was a problem hiding this comment.
Critical: Main method must be public static.
The main method signature is incorrect. Java requires the entry point to be public static void main(String[] args). Without the public static modifiers, the JVM cannot locate or execute this method, preventing the application from starting.
Apply this diff to fix the signature:
- void main(String[] args) {
+ public static void main(String[] args) {
SpringApplication.run(Exercise2025Application.class, args);
}📝 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.
| void main(String[] args) { | |
| SpringApplication.run(Exercise2025Application.class, args); | |
| } | |
| public static void main(String[] args) { | |
| SpringApplication.run(Exercise2025Application.class, args); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/example/Exercise2025Application.java around lines 9 to 11,
the main method is missing the required modifiers — change the method signature
to public static void main(String[] args) so the JVM can find and execute the
entry point, leaving the existing SpringApplication.run(...) call intact.
|
|
||
| private String username; | ||
| private String email; | ||
| private String password; |
There was a problem hiding this comment.
Password should be hashed, not stored as plain text.
The password field stores credentials as plain text, which is a security risk. Even in an exercise project, it's good practice to hash passwords using BCrypt or a similar algorithm before storing them in the database.
Consider adding password encoding in the service layer before persisting users, or use Spring Security's PasswordEncoder bean to hash passwords.
🤖 Prompt for AI Agents
In src/main/java/org/example/model/User.java around line 17 the password field
is stored in plain text; change handling so passwords are hashed before
persistence: keep the String field but do not save raw values — inject a
PasswordEncoder (e.g. BCryptPasswordEncoder via Spring Security) into the
service that creates/updates users and call encoder.encode(rawPassword) before
setting the entity’s password, use encoder.matches(rawPassword, storedHash) for
authentication, ensure the password getter is not serialized/exposed (e.g.
@JsonIgnore or remove from DTOs) and never log plain passwords.
| public Workout createWorkout(Workout workout) { | ||
|
|
||
| int calories = workout.getDuration() * 10; | ||
| workout.setCaloriesBurned(calories); | ||
|
|
||
| return workoutRepository.save(workout); | ||
| } |
There was a problem hiding this comment.
Guard against NPE when duration is null.
If workout.getDuration() returns null, line 28 will throw a NullPointerException during unboxing. Add validation or a null check before the calculation.
Apply this diff to add a null check:
@Override
public Workout createWorkout(Workout workout) {
-
- int calories = workout.getDuration() * 10;
+ Integer duration = workout.getDuration();
+ int calories = (duration != null) ? duration * 10 : 0;
workout.setCaloriesBurned(calories);
return workoutRepository.save(workout);
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/org/example/service/workout/WorkoutServiceImpl.java around
lines 26 to 32, guard against a NullPointerException when calling
workout.getDuration(): check if workout.getDuration() == null before computing
calories; if null, either throw an IllegalArgumentException with a clear message
(e.g., "duration must not be null") or treat it as 0 (set calories to 0), then
proceed to setCaloriesBurned and save; ensure you do not perform arithmetic on a
null Integer.
| <form th:action="@{/workouts/add}" method="post"> | ||
| Activity: <input type="text" name="name" required/> | ||
| Duration (min): <input type="number" name="duration" required/> | ||
| Date: <input type="date" name="date" required/> | ||
| <button type="submit" class="button">Add</button> | ||
| </form> |
There was a problem hiding this comment.
Missing caloriesBurned input field in the add form.
The table displays a "Calories" column (line 14) and shows workout.caloriesBurned (line 22), but the add workout form doesn't include an input field for this value. This inconsistency will likely result in workouts being created with null or default calorie values.
Apply this diff to add the missing field:
<form th:action="@{/workouts/add}" method="post">
Activity: <input type="text" name="name" required/>
Duration (min): <input type="number" name="duration" required/>
+ Calories Burned: <input type="number" name="caloriesBurned" required/>
Date: <input type="date" name="date" required/>
<button type="submit" class="button">Add</button>
</form>📝 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.
| <form th:action="@{/workouts/add}" method="post"> | |
| Activity: <input type="text" name="name" required/> | |
| Duration (min): <input type="number" name="duration" required/> | |
| Date: <input type="date" name="date" required/> | |
| <button type="submit" class="button">Add</button> | |
| </form> | |
| <form th:action="@{/workouts/add}" method="post"> | |
| Activity: <input type="text" name="name" required/> | |
| Duration (min): <input type="number" name="duration" required/> | |
| Calories Burned: <input type="number" name="caloriesBurned" required/> | |
| Date: <input type="date" name="date" required/> | |
| <button type="submit" class="button">Add</button> | |
| </form> |
🤖 Prompt for AI Agents
In src/main/resources/templates/workouts.html around lines 33 to 38, the
add-workout form is missing an input for caloriesBurned which causes new
workouts to lack calorie data; update the form to include a numeric input named
"caloriesBurned" (e.g., label "Calories:" with <input type="number"
name="caloriesBurned" required min="0" step="1">) placed with the other fields
before the submit button so the calories value is submitted with the form and
matches the table's Calories column.
Added User and Workout entities.
Controllers for User and Workout to handle GET, POST, and DELETE requests
Basic login implemented with Spring Security.
Security tests ensure only authorized users can access endpoints.
Controller tests for GET, POST, and DELETE endpoints.
Thymeleaf views and simple CSS for frontend.
Summary by CodeRabbit
Release Notes
New Features
Infrastructure
Tests
✏️ Tip: You can customize this high-level summary in your review settings.