Skip to content

Jorlindstrom/exercise8#216

Open
Jorlindstrom wants to merge 8 commits intomainfrom
Jorlindstrom/exercise8
Open

Jorlindstrom/exercise8#216
Jorlindstrom wants to merge 8 commits intomainfrom
Jorlindstrom/exercise8

Conversation

@Jorlindstrom
Copy link
Copy Markdown

@Jorlindstrom Jorlindstrom commented Nov 21, 2025

I have made a small backend test version of my intended thesis. A dynamic camp registration with a focus on food logistics. A central function is the management of allergy information with the ability to filter and sort participants by allergy type and organizational affiliation (parish/scout corps), as well as generate statistics on specific food allergies.

In this case, Thymeleaf is responsible for the HTML rendering, but in the thesis, the frontend tool Angular will be used.

User is admin password is admin123 for this project. I have not spent much time trying to hide it.

There are tests for REST controller endpoints and Security access control. All endpoints for the project can be found in the readme file

Skärmavbild 2025-11-21 kl  21 49 08 Skärmavbild 2025-11-21 kl  21 50 00

Summary by CodeRabbit

  • New Features

    • Full Event Management UI with create/edit/delete and detailed event pages
    • REST APIs for events, participants, patrols, allergens, and registrations
    • Allergy reporting, statistics, and CSV export
    • Role-based login and protected endpoints
  • Infrastructure

    • MySQL-backed app with DB migrations and Docker Compose support
    • Application and test configuration for local/in-memory testing
  • Tests

    • Extensive security and controller tests
  • Documentation / Chores

    • README updated with login info; repository ignore rules expanded for IDE/build artifacts

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 21, 2025

Walkthrough

This PR converts a Java template into a Spring Boot Event Management System: adds Spring Boot parent and dependencies, security, JPA entities and repositories, services, REST and web controllers, Flyway migrations, Thymeleaf templates, Docker Compose for MySQL, application configs, and extensive tests.

Changes

Cohort / File(s) Change Summary
Build & Repo config
\.gitignore, pom.xml, docker-compose.yml, README.md
Replaced .gitignore with broader Maven/IDE/os ignores; switched POM to Spring Boot parent, updated dependencies/plugins; added docker-compose.yml for MySQL; README rewritten with login info and API endpoint listings.
Application bootstrap
src/main/java/exercise8/Exercise8.java, src/main/java/org/example/Exercise2025Application.java, src/main/java/org/example/App.java
Added new Spring Boot entry exercise8.Exercise8; removed legacy org.example.App; added Exercise2025Application with non-standard main signature.
Security & configs
src/main/java/exercise8/config/SecurityConfig.java, src/main/resources/application.properties, src/test/resources/application-test.properties, src/test/resources/application.properties
Introduced Spring Security config (form + HTTP Basic, public/API rules, in-memory admin); added main and test application properties (MySQL/Flyway/H2/test creds, logging, Thymeleaf).
Entities & enums
src/main/java/exercise8/entity/*
Added JPA entities: Allergen, Event, Participant, Patrol, Registration and enums RegistrationStatus, AllergenSeverity; relationships (OneToMany, ManyToOne, ManyToMany) and validation annotations.
Repositories
src/main/java/exercise8/repository/*
Added Spring Data JPA repositories with derived queries and custom @Query methods (AllergenRepository, EventRepository, ParticipantRepository, PatrolRepository, RegistrationRepository).
Services
src/main/java/exercise8/service/*
Implemented service layer (AllergenService, EventService, ParticipantService, PatrolService, RegistrationService) with transactional CRUD, capacity checks, allergy aggregation/statistics, and helper types (ParticipantAllergyInfo).
REST API controllers
src/main/java/exercise8/controller/api/*
Added REST controllers for allergens, events, participants, patrols, registrations; Event controller exposes allergy report, CSV export and statistics; Registration controller supports confirm/cancel.
Web MVC controllers
src/main/java/exercise8/controller/web/*
Added EventController and HomeController for Thymeleaf views (list, detail, form, allergy-report) and simple redirects.
Exception handling
src/main/java/exercise8/exception/*
Added GlobalExceptionHandler, ResourceNotFoundException, RegistrationFullException with mapped HTTP statuses.
Flyway migrations & seed
src/main/resources/db/migration/V1__create_base_tables.sql, V2__insert_sample_data.sql
Added schema migration creating patrol, allergen, event, participant, registration, participant_allergen tables and a V2 seed script inserting sample data.
Thymeleaf templates
src/main/resources/templates/events/*, src/main/resources/templates/login.html
Added templates: events list/detail/form/allergy-report and login page with embedded JS/CSS and role-restricted UI elements.
Tests
src/test/java/exercise8/**, src/test/java/exercise8/security/**, src/test/java/org/example/*
Added WebMvc and security tests for controllers and auth flows; removed legacy trivial tests under org.example.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as RegistrationRestController
    participant Service as RegistrationService
    participant EventSvc as EventService
    participant Repo as RegistrationRepository
    participant DB

    Client->>API: POST /api/registrations
    API->>Service: create(registration)
    Service->>Repo: existsByEventIdAndParticipantId(...)
    Repo-->>Service: false
    Service->>EventSvc: getEventById(eventId)
    EventSvc-->>Service: Event
    Service->>Repo: countConfirmedByEventId(eventId)
    Repo-->>Service: confirmedCount
    alt capacity exceeded
        Service-->>API: throw RegistrationFullException
        API-->>Client: 400 Bad Request
    else capacity available
        Service->>Repo: save(registration)
        Repo->>DB: INSERT registration
        DB-->>Repo: success
        Repo-->>Service: Registration
        Service-->>API: Registration (201)
        API-->>Client: 201 Created
    end
Loading
sequenceDiagram
    participant Client
    participant API as EventRestController
    participant Service as EventService
    participant EventRepo as EventRepository
    participant ParticipantRepo as ParticipantRepository

    Client->>API: GET /api/events/{id}/allergy-report
    API->>Service: getAllergyReportForEvent(id)
    Service->>EventRepo: findById(id)
    EventRepo-->>Service: Event
    loop for each registration
        Service->>ParticipantRepo: load participant
        ParticipantRepo-->>Service: Participant with allergens
        Service->>Service: aggregate participant per allergen
    end
    Service-->>API: Map<String, List<ParticipantAllergyInfo>>
    API-->>Client: 200 OK (JSON)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • RegistrationService: duplicate-registration checks, capacity logic, transactional boundaries and exception types.
  • EventService: allergy aggregation, sorting, null-safe handling of relationships and registration filtering.
  • EventRestController: CSV export encoding/escaping, attachment headers and filename generation.
  • ParticipantRepository: custom JPQL/@query methods (joins and parameter binding).
  • SecurityConfig: endpoint protection, CSRF exemptions for API, and authentication entry points.
  • Flyway migrations & V2 seed: referential integrity, FK cascade rules, and sample data consistency with entity constraints.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Jorlindstrom/exercise8

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
Copy Markdown

@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: 6

🧹 Nitpick comments (28)
src/main/java/exercise8/entity/AllergenSeverity.java (1)

3-7: Remove unused enum or translate comments to English.

Verification confirms the enum is unused throughout the codebase—it appears only in its definition. Combined with Swedish comments in an English codebase, consider:

  1. Remove the enum if it's not needed for this exercise, OR
  2. Translate comments to English for consistency if the enum will be used:
    -    INFO,      // Informativ, mindre allvarlig
    -    WARNING,   // Varning, kan ge obehag
    -    CRITICAL   // Kritisk, livshotande
    +    INFO,      // Informational, less severe
    +    WARNING,   // Warning, may cause discomfort
    +    CRITICAL   // Critical, life-threatening
src/main/java/exercise8/exception/GlobalExceptionHandler.java (1)

16-56: Consider extracting common error response logic.

All three exception handlers follow the same pattern for building the error response body. Extracting this logic into a helper method would reduce duplication and make the code more maintainable.

Add a helper method to the class:

private Map<String, Object> buildErrorBody(HttpStatus status, String error, String message, WebRequest request) {
    Map<String, Object> body = new HashMap<>();
    body.put("timestamp", LocalDateTime.now());
    body.put("status", status.value());
    body.put("error", error);
    body.put("message", message);
    body.put("path", request.getDescription(false).replace("uri=", ""));
    return body;
}

Then simplify each handler:

 @ExceptionHandler(ResourceNotFoundException.class)
 public ResponseEntity<Map<String, Object>> handleResourceNotFoundException(
         ResourceNotFoundException ex, WebRequest request) {
-
-    Map<String, Object> body = new HashMap<>();
-    body.put("timestamp", LocalDateTime.now());
-    body.put("status", HttpStatus.NOT_FOUND.value());
-    body.put("error", "Not Found");
-    body.put("message", ex.getMessage());
-    body.put("path", request.getDescription(false).replace("uri=", ""));
-
+    Map<String, Object> body = buildErrorBody(HttpStatus.NOT_FOUND, "Not Found", ex.getMessage(), request);
     return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
 }

Apply similar changes to the other two handlers.

README.md (2)

1-4: Minor formatting: Add space after colons.

For improved readability, add a space after the colons in the login information.

Apply this diff:

 ### LoginInformation
-- User:admin
-- Lösen:admin123
+- User: admin
+- Lösen: admin123
 - Som oinloggad så omdirigeras du automatiskt till login.

8-47: Consider adding language identifiers to code blocks.

The markdown linter flags that fenced code blocks should have a language specified. While the blocks contain plain text endpoint lists, adding text as the language identifier would satisfy linters and improve consistency.

Example for the first block:

-```
+```text
 - GET    /api/events                    - Lista alla events

Apply similar changes to the other code blocks at lines 16, 24, 31, and 39.

src/test/resources/application.properties (1)

1-29: Consider consolidating test configuration files.

Having both application.properties and application-test.properties in src/test/resources with different admin credentials (admin/admin123 vs testadmin/testpass123) creates potential confusion. Tests using @ActiveProfiles("test") will use the credentials from application-test.properties, but developers might expect the credentials from this file.

Consider either:

  • Using only application-test.properties for test configuration
  • Documenting clearly which profile uses which credentials
  • Consolidating to a single test configuration file
src/main/resources/templates/events/form.html (1)

305-313: Minor: Inconsistent comment language.

Line 305 has a Swedish comment while Line 313 has an English comment. For consistency, consider using the same language throughout the codebase.

src/test/java/exercise8/security/BasicSecurityTest.java (1)

1-47: Consider consolidating duplicate security tests.

BasicSecurityTest and ComprehensiveSecurityTest have overlapping test coverage:

  • Login page accessibility
  • API authentication scenarios (no auth, valid auth, invalid password)

Since ComprehensiveSecurityTest covers all scenarios in BasicSecurityTest plus additional cases, you could remove this file or add a comment explaining that it serves as a simpler example for learning purposes.

src/main/java/exercise8/entity/Registration.java (1)

34-37: Consider using @PrePersist or Hibernate's @CreationTimestamp for registration timestamp.

Setting registrationDate = LocalDateTime.now() in the constructor captures the time when the object is instantiated in memory, not when it's persisted to the database. This can lead to inaccurate timestamps if there's a delay between object creation and database commit.

Apply this pattern instead:

+import org.hibernate.annotations.CreationTimestamp;
+
 @Entity
 @Table(name = "registration",
         uniqueConstraints = @UniqueConstraint(columnNames = {"event_id", "participant_id"}))
 public class Registration {
 
     // ... other fields ...
 
+    @CreationTimestamp
     @Column(name = "registration_date")
     private LocalDateTime registrationDate;
 
     // ... 
     
     public Registration() {
-        this.registrationDate = LocalDateTime.now();
         this.status = RegistrationStatus.PENDING;
     }

Alternatively, use @PrePersist:

@PrePersist
protected void onCreate() {
    if (registrationDate == null) {
        registrationDate = LocalDateTime.now();
    }
}
src/main/resources/templates/events/list.html (1)

440-452: Consider simplifying backdrop click handling.

The manual bounding rectangle calculation for backdrop clicks works but is verbose. Modern browsers support checking if the click target is the dialog itself:

-    // Close on click on backdrop
     deleteDialog.addEventListener('click', (event) => {
-        const rect = deleteDialog.getBoundingClientRect();
-        const isInDialog = (
-            rect.top <= event.clientY &&
-            event.clientY <= rect.top + rect.height &&
-            rect.left <= event.clientX &&
-            event.clientX <= rect.left + rect.width
-        );
-
-        if (!isInDialog) {
+        // Close when clicking on the backdrop (the dialog element itself, not its content)
+        if (event.target === deleteDialog) {
             closeDeleteDialog();
         }
     });
src/main/resources/templates/events/detail.html (1)

472-485: Consider simplifying backdrop click handling (same as in list.html).

The backdrop click logic can be simplified by checking if the click target is the dialog element itself:

-    // Close on click on backdrop
     deleteDialog.addEventListener('click', (event) => {
-        const rect = deleteDialog.getBoundingClientRect();
-        const isInDialog = (
-            rect.top <= event.clientY &&
-            event.clientY <= rect.top + rect.height &&
-            rect.left <= event.clientX &&
-            event.clientX <= rect.left + rect.width
-        );
-
-        if (!isInDialog) {
+        if (event.target === deleteDialog) {
             closeDeleteDialog();
         }
     });
src/main/java/exercise8/controller/api/PatrolRestController.java (1)

20-62: Patrol REST controller looks correct; only minor style refinements possible

The CRUD and district endpoints are consistent with the other REST controllers and correctly delegate to PatrolService, with proper status codes and @Valid usage. If you want to tighten things up later, consider switching @Autowired field injection to constructor injection and adding a Location header on POST /api/patrols to point to the created resource URL, but these are purely optional polish items.

src/test/java/exercise8/controller/EventRestControllerSimpleTest.java (1)

22-70: WebMvc tests are solid; consider aligning security config with the main REST test

The three tests exercise happy‑path and unauthenticated access correctly and the mocking of EventService is clean. Given you already have EventRestControllerTest that imports your SecurityConfig, you might consider also importing that here (or clearly documenting this as a lighter smoke test) so both suites run against the same security setup, but that’s optional.

src/main/java/exercise8/service/AllergenService.java (1)

19-49: Allergen service logic is correct; consider cleaning up the removed description field

The CRUD methods are straightforward and the use of ResourceNotFoundException in findById/update is appropriate. Since the allergen table no longer has a description column, you can safely delete the commented setDescription line to avoid future confusion about non‑existent fields.

src/main/java/exercise8/config/SecurityConfig.java (1)

23-81: Security setup matches requirements; only minor, optional DI/style tweaks

The split between public pages, authenticated web, and /api/** (with HTTP Basic + 401 vs redirect) looks coherent, and the in‑memory admin user wired from app.admin.* is reasonable for this exercise project (hard‑coded creds are expected here). If you want to tighten things up later, you could switch the @Value fields to constructor injection for consistency with other beans, but that’s purely stylistic here.

src/main/java/exercise8/controller/api/EventRestController.java (1)

56-89: CSV export works; consider basic escaping and narrower error handling

The CSV generation is fine for controlled data, but if allergyName or any participant fields can contain quotes, commas, or newlines, the current String.format will produce malformed CSV. You might later add minimal escaping (e.g. replace " with "" before wrapping in quotes).

Similarly, catching plain RuntimeException and returning 404 for everything will also turn internal bugs into “not found”. If you introduce a specific exception for “event not found” (or reuse EntityNotFoundException), you can catch that here and let other exceptions bubble up as 500s.

src/main/java/exercise8/repository/RegistrationRepository.java (1)

31-33: Prefer a derived count method over hard‑coded 'CONFIRMED' in JPQL

Instead of the custom query with r.status = 'CONFIRMED', you can rely on a derived query:

-// Count the number of confirmed registrations for an event
-@Query("SELECT COUNT(r) FROM Registration r WHERE r.event.id = :eventId AND r.status = 'CONFIRMED'")
-long countConfirmedByEventId(@Param("eventId") Long eventId);
+// Count the number of registrations for an event with a given status
+long countByEventIdAndStatus(Long eventId, RegistrationStatus status);

and call it with RegistrationStatus.CONFIRMED. That keeps things type‑safe and automatically in sync if the enum or mapping changes.

src/main/java/exercise8/entity/Participant.java (1)

10-130: Participant JPA mapping and JSON configuration look consistent

Field mappings, relationships (patrol, registrations, allergens), and @JsonIgnoreProperties usage align well with Patrol and Registration, avoiding circular serialization while keeping useful navigation. Initializing collections to new ArrayList<>/new HashSet<> is also a good defensive choice. If you later introduce Bean Validation, you could optionally add annotations like @NotBlank/@Email on firstName/lastName/email, but the current implementation is perfectly fine for this exercise.

src/main/java/exercise8/service/ParticipantService.java (1)

19-70: ParticipantService CRUD and query logic are straightforward and consistent

The service follows the same clear pattern as RegistrationService/PatrolService: centralized findById/findByEmail with ResourceNotFoundException, simple create, and an update that copies all relevant participant fields (including patrol and allergens) before saving. findByPatrolId and searchByName are thin, appropriate delegates to the repository. As an optional hardening step, you could null out any incoming ID in create to guard against accidental updates via POST, but for this project’s use it’s not required.

src/main/java/exercise8/entity/Event.java (1)

9-101: Event entity design matches the rest of the domain model

The Event mapping (dates, location, maxParticipants) and the @OneToMany registrations side are consistent with the Registration entity and use @JsonIgnoreProperties appropriately to prevent circular references. This is a clean, minimal aggregate root for the event domain. If you ever target a database where event is a reserved word, you might optionally rename the table, but for this exercise setup it’s acceptable.

src/main/java/exercise8/controller/api/ParticipantRestController.java (1)

17-18: Prefer constructor injection over field injection for controllers

Using field injection makes testing and immutability harder and is inconsistent with EventRestController, which uses constructor injection. Consider switching to constructor injection for ParticipantService here as well.

src/main/java/exercise8/service/PatrolService.java (1)

16-17: Optional: align DI style with constructor-based injection

PatrolService uses field injection; elsewhere (EventService) you already use constructor injection, which is generally preferred for immutability and easier testing. Not required for this exercise, but you may consider refactoring to a constructor-injected PatrolRepository for consistency.

src/main/java/exercise8/service/EventService.java (1)

31-36: Unify “event not found” handling (RuntimeException vs ResourceNotFoundException)

EventService throws plain RuntimeException in getEventById, getAllergyReportForEvent, and getAllergyStatisticsForEvent, but uses ResourceNotFoundException in deleteEvent. This leads to inconsistent error behavior across callers and forces EventRestController to catch RuntimeException explicitly.

Consider standardizing on ResourceNotFoundException("Event", "id", id) (or similar) for all “event not found” cases, and then letting your global exception handler map that to a 404. That would also simplify EventRestController, which could drop the try/catch blocks and rely on centralized exception handling.

Also applies to: 47-51, 66-69, 111-114

src/main/java/exercise8/controller/api/RegistrationRestController.java (1)

17-18: Consider constructor injection for RegistrationService

For consistency with EventRestController and better testability/immutability, consider switching from field injection to constructor injection for RegistrationService.

src/main/java/exercise8/controller/web/EventController.java (3)

23-78: CRUD flow looks solid; consider explicit handling for missing events

The create/update paths with @Valid + BindingResult and redirect patterns look good and idiomatic for a Thymeleaf-backed form flow. One potential gap is what happens if eventService.getEventById(id) does not find an event (for view/edit/update):

  • If it returns null, later code will NPE.
  • If it throws (e.g. custom not-found), the user likely gets a generic error page.

Consider standardizing this into a clearer UX, e.g. having getEventById return an Optional and translating “not found” into a 404 view or a redirect to /events with a flash error message. This keeps user feedback predictable and avoids hard-to-understand generic errors.


80-91: Delete handler is clear; you may want more specific exception typing

The delete handler correctly uses RedirectAttributes to surface success/failure messages and redirects to appropriate views. To make failure cases more self-documenting and avoid accidentally catching unrelated IllegalStateExceptions, consider replacing IllegalStateException with a domain-specific exception type from EventService (e.g. EventDeletionNotAllowedException) and catching that here. This keeps the controller logic explicit about what kind of failure it’s prepared to handle.


94-107: Allergy report endpoint is straightforward; watch out for heavy graphs/N+1

The allergy-report endpoint nicely aggregates event, allergyReport, and statistics into the model. Depending on how eventService.getAllergyReportForEvent and getAllergyStatisticsForEvent are implemented, this view could trigger:

  • Large object graphs (e.g. deeply nested participants/registrations) being loaded and passed to the template.
  • Potential N+1 query patterns if related collections are lazily initialized per row during rendering.

If you notice performance issues as data grows, it may be worth introducing dedicated DTOs or optimized queries for this endpoint so only the data needed for the report is loaded.

src/main/java/exercise8/entity/Patrol.java (1)

27-87: JPA mapping is consistent; consider tightening JSON exposure and equality semantics

The @OneToMany(mappedBy = "patrol") plus the Participant side @ManyToOne mapping look consistent, and initializing participants with an empty ArrayList is a good default.

A couple of optional improvements:

  • The combination of @JsonIgnoreProperties({"patrol", "registrations"}) on participants here and @JsonIgnoreProperties({"participants"}) on Participant.patrol will prevent the most obvious recursion, but participants can still serialize a fairly large graph (e.g. via other relationships like allergens). If your REST APIs don’t need full participant details when fetching a patrol, consider trimming this further (DTOs, projections, or ignoring the collection entirely) to avoid heavy payloads.
  • If Patrol instances are ever kept in Sets/Maps outside a pure JPA context, consider adding equals/hashCode based on the identifier (or a natural key) to avoid subtle collection behaviour issues.

Both are non-blocking but can improve robustness as the domain grows.

pom.xml (1)

86-127: Test dependency stack is comprehensive; a few entries may be redundant

The test setup (Boot test starter, security test, H2, Testcontainers, MySQL module) is robust and well-aligned with integration testing against a real database.

Minor clean-up you might consider:

  • spring-boot-starter-test already pulls in JUnit Jupiter; the explicit org.testcontainers:junit-jupiter is needed for Testcontainers integration, but an explicit generic junit-jupiter dependency (if you add one elsewhere) is usually unnecessary.
  • Spring Boot’s dependency management will typically handle versions for spring-boot-testcontainers and the core testcontainers modules, so you don’t need to specify versions manually (which you are correctly not doing).

These are non-blocking, just opportunities to trim the POM a bit if you feel like tidying.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8618163 and bfaa6fd.

📒 Files selected for processing (53)
  • .gitignore (1 hunks)
  • README.md (1 hunks)
  • docker-compose.yml (1 hunks)
  • pom.xml (1 hunks)
  • src/main/java/exercise8/Exercise8.java (1 hunks)
  • src/main/java/exercise8/config/SecurityConfig.java (1 hunks)
  • src/main/java/exercise8/controller/api/AllergenRestController.java (1 hunks)
  • src/main/java/exercise8/controller/api/EventRestController.java (1 hunks)
  • src/main/java/exercise8/controller/api/ParticipantRestController.java (1 hunks)
  • src/main/java/exercise8/controller/api/PatrolRestController.java (1 hunks)
  • src/main/java/exercise8/controller/api/RegistrationRestController.java (1 hunks)
  • src/main/java/exercise8/controller/web/EventController.java (1 hunks)
  • src/main/java/exercise8/controller/web/HomeController.java (1 hunks)
  • src/main/java/exercise8/entity/Allergen.java (1 hunks)
  • src/main/java/exercise8/entity/AllergenSeverity.java (1 hunks)
  • src/main/java/exercise8/entity/Event.java (1 hunks)
  • src/main/java/exercise8/entity/Participant.java (1 hunks)
  • src/main/java/exercise8/entity/Patrol.java (1 hunks)
  • src/main/java/exercise8/entity/Registration.java (1 hunks)
  • src/main/java/exercise8/entity/RegistrationStatus.java (1 hunks)
  • src/main/java/exercise8/exception/GlobalExceptionHandler.java (1 hunks)
  • src/main/java/exercise8/exception/RegistrationFullException.java (1 hunks)
  • src/main/java/exercise8/exception/ResourceNotFoundException.java (1 hunks)
  • src/main/java/exercise8/repository/AllergenRepository.java (1 hunks)
  • src/main/java/exercise8/repository/EventRepository.java (1 hunks)
  • src/main/java/exercise8/repository/ParticipantRepository.java (1 hunks)
  • src/main/java/exercise8/repository/PatrolRepository.java (1 hunks)
  • src/main/java/exercise8/repository/RegistrationRepository.java (1 hunks)
  • src/main/java/exercise8/service/AllergenService.java (1 hunks)
  • src/main/java/exercise8/service/EventService.java (1 hunks)
  • src/main/java/exercise8/service/ParticipantService.java (1 hunks)
  • src/main/java/exercise8/service/PatrolService.java (1 hunks)
  • src/main/java/exercise8/service/RegistrationService.java (1 hunks)
  • src/main/java/org/example/App.java (0 hunks)
  • src/main/java/org/example/Exercise2025Application.java (1 hunks)
  • src/main/resources/application.properties (1 hunks)
  • src/main/resources/db/migration/V1__create_base_tables.sql (1 hunks)
  • src/main/resources/db/migration/V2__insert_sample_data.sql (1 hunks)
  • src/main/resources/templates/events/allergy-report.html (1 hunks)
  • src/main/resources/templates/events/detail.html (1 hunks)
  • src/main/resources/templates/events/form.html (1 hunks)
  • src/main/resources/templates/events/list.html (1 hunks)
  • src/main/resources/templates/login.html (1 hunks)
  • src/test/java/exercise8/controller/EventRestControllerSimpleTest.java (1 hunks)
  • src/test/java/exercise8/controller/api/AllergenRestControllerTest.java (1 hunks)
  • src/test/java/exercise8/controller/api/EventRestControllerTest.java (1 hunks)
  • src/test/java/exercise8/controller/api/ParticipantRestControllerTest.java (1 hunks)
  • src/test/java/exercise8/security/BasicSecurityTest.java (1 hunks)
  • src/test/java/exercise8/security/ComprehensiveSecurityTest.java (1 hunks)
  • src/test/java/org/example/AppIT.java (0 hunks)
  • src/test/java/org/example/AppTest.java (0 hunks)
  • src/test/resources/application-test.properties (1 hunks)
  • src/test/resources/application.properties (1 hunks)
💤 Files with no reviewable changes (3)
  • src/main/java/org/example/App.java
  • src/test/java/org/example/AppTest.java
  • src/test/java/org/example/AppIT.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-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:

  • README.md
  • src/main/java/org/example/Exercise2025Application.java
📚 Learning: 2025-09-19T11:36:59.329Z
Learnt from: LolloGro
Repo: fungover/exercise2025 PR: 106
File: src/main/java/exercise4/repository/InMemoryProductRepository.java:0-0
Timestamp: 2025-09-19T11:36:59.329Z
Learning: In the InMemoryProductRepository class in src/main/java/exercise4/repository/InMemoryProductRepository.java, the updateProduct method implements a versioning system where multiple products with the same ID can exist, but identical product objects are rejected using contains() check. This allows maintaining product history while preventing duplicate identical entries.

Applied to files:

  • src/main/java/exercise8/repository/ParticipantRepository.java
  • src/main/java/exercise8/repository/PatrolRepository.java
🧬 Code graph analysis (24)
src/main/java/exercise8/entity/Participant.java (3)
src/main/java/exercise8/entity/Allergen.java (1)
  • Entity (6-42)
src/main/java/exercise8/entity/Patrol.java (1)
  • Entity (8-88)
src/main/java/exercise8/entity/Registration.java (1)
  • Entity (6-93)
src/main/java/exercise8/service/AllergenService.java (2)
src/main/java/exercise8/service/EventService.java (1)
  • Service (14-161)
src/main/java/exercise8/service/ParticipantService.java (1)
  • Service (12-71)
src/main/java/exercise8/service/ParticipantService.java (2)
src/main/java/exercise8/service/PatrolService.java (1)
  • Service (12-57)
src/main/java/exercise8/service/RegistrationService.java (1)
  • Service (14-106)
src/main/java/exercise8/exception/ResourceNotFoundException.java (1)
src/main/java/exercise8/exception/RegistrationFullException.java (1)
  • ResponseStatus (6-16)
src/main/java/exercise8/controller/api/ParticipantRestController.java (3)
src/main/java/exercise8/controller/api/AllergenRestController.java (1)
  • RestController (14-57)
src/main/java/exercise8/controller/api/PatrolRestController.java (1)
  • RestController (13-63)
src/main/java/exercise8/controller/api/RegistrationRestController.java (1)
  • RestController (13-86)
src/test/java/exercise8/controller/api/ParticipantRestControllerTest.java (2)
src/test/java/exercise8/controller/EventRestControllerSimpleTest.java (1)
  • WebMvcTest (22-71)
src/test/java/exercise8/controller/api/AllergenRestControllerTest.java (1)
  • WebMvcTest (29-280)
src/main/java/exercise8/entity/Event.java (2)
src/main/java/exercise8/entity/Participant.java (1)
  • Entity (10-131)
src/main/java/exercise8/entity/Registration.java (1)
  • Entity (6-93)
src/main/java/exercise8/entity/Registration.java (2)
src/main/java/exercise8/entity/Event.java (1)
  • Entity (9-102)
src/main/java/exercise8/entity/Participant.java (1)
  • Entity (10-131)
src/test/java/exercise8/security/BasicSecurityTest.java (1)
src/test/java/exercise8/security/ComprehensiveSecurityTest.java (1)
  • SpringBootTest (18-142)
src/main/java/exercise8/entity/Allergen.java (1)
src/main/java/exercise8/entity/Participant.java (1)
  • Entity (10-131)
src/test/java/exercise8/controller/api/AllergenRestControllerTest.java (2)
src/test/java/exercise8/controller/api/EventRestControllerTest.java (1)
  • WebMvcTest (30-201)
src/test/java/exercise8/controller/api/ParticipantRestControllerTest.java (1)
  • WebMvcTest (29-328)
src/main/java/exercise8/controller/api/PatrolRestController.java (2)
src/main/java/exercise8/controller/api/AllergenRestController.java (1)
  • RestController (14-57)
src/main/java/exercise8/controller/api/ParticipantRestController.java (1)
  • RestController (13-66)
src/main/java/exercise8/service/PatrolService.java (2)
src/main/java/exercise8/service/AllergenService.java (1)
  • Service (12-50)
src/main/java/exercise8/service/ParticipantService.java (1)
  • Service (12-71)
src/main/java/exercise8/service/RegistrationService.java (2)
src/main/java/exercise8/service/EventService.java (1)
  • Service (14-161)
src/main/java/exercise8/service/ParticipantService.java (1)
  • Service (12-71)
src/main/java/exercise8/controller/web/HomeController.java (1)
src/main/java/exercise8/controller/web/EventController.java (1)
  • Controller (12-108)
src/main/java/exercise8/controller/web/EventController.java (1)
src/main/java/exercise8/controller/web/HomeController.java (1)
  • Controller (6-23)
src/main/java/exercise8/entity/Patrol.java (1)
src/main/java/exercise8/entity/Participant.java (1)
  • Entity (10-131)
src/main/java/exercise8/controller/api/RegistrationRestController.java (2)
src/main/java/exercise8/controller/api/EventRestController.java (1)
  • RestController (15-106)
src/main/java/exercise8/controller/api/ParticipantRestController.java (1)
  • RestController (13-66)
src/main/java/exercise8/service/EventService.java (2)
src/main/java/exercise8/service/ParticipantService.java (1)
  • Service (12-71)
src/main/java/exercise8/service/RegistrationService.java (1)
  • Service (14-106)
src/test/java/exercise8/controller/api/EventRestControllerTest.java (2)
src/test/java/exercise8/controller/EventRestControllerSimpleTest.java (1)
  • WebMvcTest (22-71)
src/test/java/exercise8/controller/api/AllergenRestControllerTest.java (1)
  • WebMvcTest (29-280)
src/test/java/exercise8/controller/EventRestControllerSimpleTest.java (1)
src/test/java/exercise8/controller/api/EventRestControllerTest.java (1)
  • WebMvcTest (30-201)
src/test/java/exercise8/security/ComprehensiveSecurityTest.java (1)
src/test/java/exercise8/security/BasicSecurityTest.java (1)
  • SpringBootTest (14-47)
src/main/java/exercise8/exception/RegistrationFullException.java (1)
src/main/java/exercise8/exception/ResourceNotFoundException.java (1)
  • ResponseStatus (6-16)
src/main/java/exercise8/controller/api/AllergenRestController.java (4)
src/main/java/exercise8/controller/api/EventRestController.java (1)
  • RestController (15-106)
src/main/java/exercise8/controller/api/ParticipantRestController.java (1)
  • RestController (13-66)
src/main/java/exercise8/controller/api/PatrolRestController.java (1)
  • RestController (13-63)
src/main/java/exercise8/controller/api/RegistrationRestController.java (1)
  • RestController (13-86)
🪛 markdownlint-cli2 (0.18.1)
README.md

8-8: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


16-16: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


24-24: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


39-39: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (22)
.gitignore (1)

1-32: LGTM!

The expanded .gitignore appropriately covers Maven build artifacts, IDE files, OS-specific files, and logs. This is a comprehensive and well-structured ignore configuration for a Spring Boot project.

src/main/java/exercise8/exception/RegistrationFullException.java (1)

6-16: LGTM!

The exception class follows the same pattern as ResourceNotFoundException and provides both a generic and a formatted constructor for flexibility. The @ResponseStatus(HttpStatus.BAD_REQUEST) annotation appropriately maps this business logic exception to HTTP 400.

src/main/java/exercise8/controller/web/HomeController.java (1)

6-23: LGTM!

The controller provides clean navigation with appropriate redirects to the events listing and a dedicated login endpoint. The implementation aligns well with the security configuration and event controller.

src/main/java/exercise8/entity/RegistrationStatus.java (1)

3-8: LGTM!

The enum provides a clear set of registration lifecycle states. The status values (PENDING, CONFIRMED, CANCELLED, WAITLIST) appropriately model the domain requirements for camp registration management.

docker-compose.yml (1)

3-3: No issues found—MySQL 9.5.0 is a valid and available release.

MySQL 9.5.0 exists as MySQL Community Server 9.5.0 (Innovation) with release notes dated October 2025. The version specified in the docker-compose.yml file is current and valid for use.

Likely an incorrect or invalid review comment.

src/main/java/exercise8/Exercise8.java (1)

1-12: LGTM! Standard Spring Boot entry point.

The implementation follows the canonical Spring Boot application structure with proper annotations and main method.

src/main/java/exercise8/exception/ResourceNotFoundException.java (1)

1-17: LGTM! Well-structured custom exception.

The exception properly uses @ResponseStatus for automatic HTTP 404 responses and provides flexible constructors for different error scenarios.

src/test/java/exercise8/controller/api/EventRestControllerTest.java (1)

1-201: LGTM! Comprehensive test coverage.

The test suite effectively covers:

  • Authentication scenarios (unauthorized, mock user, basic auth, wrong password)
  • CRUD operations on events
  • Allergy report endpoints (JSON and CSV)
  • Error handling (404 cases)

The test structure follows Spring Boot testing best practices with proper mocking and security configuration.

Based on learnings

src/main/resources/templates/login.html (1)

1-162: LGTM! Well-designed login template.

The template provides a clean, user-friendly login interface with:

  • Proper Thymeleaf integration for Spring Security
  • Clear error and success messaging
  • Responsive layout with modern styling
  • Good form usability (autofocus, required fields)
src/main/resources/templates/events/form.html (1)

266-338: Solid client-side date validation.

The JavaScript validation effectively:

  • Prevents startDate > endDate submissions
  • Dynamically updates min/max constraints as dates change
  • Provides clear visual feedback with error messages
  • Disables submit button when dates are invalid
src/main/resources/application.properties (1)

1-31: Good configuration structure with externalized credentials.

The configuration properly demonstrates environment variable usage for sensitive credentials while providing sensible defaults for local development. The combination of Flyway for schema management and validate mode for ddl-auto ensures schema consistency.

Based on learnings: This is an exercise/learning project where simplified local development settings (like empty database password) are acceptable.

src/test/java/exercise8/security/ComprehensiveSecurityTest.java (1)

1-142: Excellent comprehensive security test coverage.

This test class thoroughly validates the security configuration with well-structured tests covering:

  • Public vs. protected endpoint access patterns
  • Form-based login for web endpoints
  • HTTP Basic authentication for API endpoints
  • CSRF protection enforcement

The separation between web (redirect to login) and API (401 Unauthorized) authentication strategies is properly tested.

src/main/java/exercise8/repository/AllergenRepository.java (1)

1-14: LGTM!

Clean Spring Data JPA repository following best practices with Optional<Allergen> return type for null safety.

src/main/resources/templates/events/allergy-report.html (1)

225-237: Simple and effective export implementation.

The JavaScript export functions appropriately use window.location.href to trigger file downloads from the API endpoints. The eventId is safely inlined from the server-side template.

src/main/resources/db/migration/V2__insert_sample_data.sql (1)

1-72: Sample data seeding matches the schema and relationships

The insert order respects foreign keys and the referenced IDs (patrols 1–4, events 1–3, participants 1–8) line up correctly with the base tables from V1. For an exercise project this looks like a good, realistic dataset to drive the UI and reports.

src/main/java/exercise8/repository/PatrolRepository.java (1)

10-20: PatrolRepository query methods are idiomatic and consistent

Extending JpaRepository plus the three derived queries (findByName, findByDistrict, findByNameContainingIgnoreCase) are all correctly named for Spring Data and match the domain needs (exact lookup, district filter, and case‑insensitive search).

src/main/resources/db/migration/V1__create_base_tables.sql (1)

1-64: Base table definitions are coherent and match the domain model

The schema for patrols, allergens, events, participants, registrations, and the participant_allergen join table is internally consistent and lines up with the relationships used in your JPA entities and services (including the unique (event_id, participant_id) registration constraint). This is a solid foundation for the rest of the application.

src/main/java/exercise8/repository/ParticipantRepository.java (1)

15-30: ParticipantRepository custom queries are well-formed and align with usage

findByEmail/findByPatrolId use standard derived queries, and the JPQL for findByAllergenId and searchByName correctly reference p.allergens, use a bound :allergenId, and perform case‑insensitive partial matching on first/last name. This should support the service and filtering features without issues.

src/test/java/exercise8/controller/api/ParticipantRestControllerTest.java (1)

62-327: Comprehensive participant REST + security tests look solid

The test suite cleanly covers unauthorized vs authorized access, happy paths, and 404 scenarios for all participant endpoints, including patrol-based lookup, with correct Mockito stubbing and verification. This is nicely aligned with the Allergen/Event controller tests and should give good confidence in the web + security layer.

src/test/java/exercise8/controller/api/AllergenRestControllerTest.java (1)

58-279: Allergen REST controller tests provide thorough coverage

The tests exercise all CRUD endpoints under both unauthenticated and authenticated scenarios, including 404 paths, and verify service interaction where it matters. The structure and naming are consistent with the other controller tests, which keeps the suite easy to follow and maintain in this exercise project.

src/main/java/exercise8/service/EventService.java (1)

63-106: Allergy report/statistics aggregation looks correct and efficient

The allergy report (getAllergyReportForEvent) and statistics (getAllergyStatisticsForEvent) methods:

  • Iterate registrations and participants’ allergens once.
  • Group by allergen name into a LinkedHashMapTreeMap to get deterministic, sorted keys.
  • Sort each participant list by last name then first name.
  • Use TreeMap + merge for the statistics view.

This is a clear and efficient implementation for the intended use case.

Also applies to: 109-124

pom.xml (1)

8-23: Java 25 and Spring Boot 3.5.6 compatibility is officially supported—no changes needed.

Verification confirms that Spring Boot 3.5.6 (released September 18, 2025) explicitly documents Java 25 support, and the combination is fully compatible. The minimum Java requirement for Spring Boot 3.5.x is Java 17, and Java 25 is officially tested and supported.

Your configuration is correct as written.

Comment thread src/main/java/exercise8/controller/api/AllergenRestController.java
Comment thread src/main/java/exercise8/entity/Allergen.java
Comment thread src/main/java/exercise8/repository/EventRepository.java Outdated
Comment thread src/main/java/exercise8/service/RegistrationService.java
Comment on lines +48 to +56
// Check if the event is available
if (event.getMaxParticipants() != null) {
long confirmedCount = registrationRepository.countConfirmedByEventId(event.getId());
if (confirmedCount >= event.getMaxParticipants()) {
throw new RegistrationFullException(event.getName(), event.getMaxParticipants());
}
}

registration.setRegistrationDate(LocalDateTime.now());
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

Event capacity can be bypassed via confirm/update without checks

Capacity is enforced only in create using countConfirmedByEventId, but:

  • confirm(Long id) can freely change status to CONFIRMED without re-checking maxParticipants.
  • update(Long id, Registration registrationDetails) can also set status to CONFIRMED (or other states) without any capacity logic.

This allows exceeding maxParticipants, e.g.:

  • Create registrations while capacity is available (or even just below it).
  • Later confirm multiple PENDING registrations, pushing CONFIRMED above the limit.

Consider centralizing the capacity check and calling it from both create and any path that transitions to CONFIRMED:

+    private void assertCapacityAvailable(Event event, RegistrationStatus targetStatus) {
+        if (event.getMaxParticipants() != null && targetStatus == RegistrationStatus.CONFIRMED) {
+            long confirmedCount = registrationRepository.countConfirmedByEventId(event.getId());
+            if (confirmedCount >= event.getMaxParticipants()) {
+                throw new RegistrationFullException(event.getName(), event.getMaxParticipants());
+            }
+        }
+    }
@@
     public Registration create(Registration registration) {
         Event event = registration.getEvent();
@@
-        // Check if the event is available
-        if (event.getMaxParticipants() != null) {
-            long confirmedCount = registrationRepository.countConfirmedByEventId(event.getId());
-            if (confirmedCount >= event.getMaxParticipants()) {
-                throw new RegistrationFullException(event.getName(), event.getMaxParticipants());
-            }
-        }
+        // Check if the event can accept another (potentially confirmed) registration
+        assertCapacityAvailable(event, RegistrationStatus.PENDING);
@@
     public Registration update(Long id, Registration registrationDetails) {
         Registration registration = findById(id);
 
-        registration.setStatus(registrationDetails.getStatus());
+        var newStatus = registrationDetails.getStatus();
+        assertCapacityAvailable(registration.getEvent(), newStatus);
+        registration.setStatus(newStatus);
@@
     public Registration confirm(Long id) {
         Registration registration = findById(id);
+        assertCapacityAvailable(registration.getEvent(), RegistrationStatus.CONFIRMED);
         registration.setStatus(RegistrationStatus.CONFIRMED);

You can adapt this sketch to your exact status model, but some form of capacity check on status transitions to CONFIRMED is important to keep the max-participant rule reliable.

Also applies to: 62-70, 72-77

Comment on lines +9 to +11
void main(String[] args) {
SpringApplication.run(Exercise2025Application.class, args);
}
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 | 🔴 Critical

Critical: Main method must be public and static.

The main method signature is incorrect. Java application entry points must be public static void main(String[] args). The current method is package-private and non-static, which will prevent the application from starting.

Apply this diff to fix the method 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.

Suggested change
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 declared as a package-private, non-static method; change its
signature to the Java entry-point signature by making it public and static
(i.e., public static void main(String[] args)) so the JVM can invoke it, leaving
the body to call SpringApplication.run(Exercise2025Application.class, args).

Copy link
Copy Markdown

@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: 0

🧹 Nitpick comments (3)
src/main/java/exercise8/controller/api/AllergenRestController.java (1)

17-18: Consider constructor injection for consistency with EventRestController.

While field injection works fine in this exercise project, switching to constructor injection would improve testability and align with the pattern used in EventRestController. That said, this approach is consistent with other REST controllers in the codebase (PatrolRestController, ParticipantRestController, RegistrationRestController).

If you'd like to refactor, apply this diff:

-    @Autowired
-    private AllergenService allergenService;
+    private final AllergenService allergenService;
+
+    public AllergenRestController(AllergenService allergenService) {
+        this.allergenService = allergenService;
+    }
src/main/java/exercise8/repository/EventRepository.java (2)

13-14: Clarify the comment to mention "after".

The comment states "Find events by a specific date" but the method finds events starting after that date. Consider updating the comment to be more precise: "Find events starting after a specific date".


22-23: Parameter order has been corrected.

The parameter order now correctly aligns with the method name (StartDate before EndDate). However, the parameter names startDate and endDate might be slightly misleading for the "ongoing events" use case described in the comment.

For finding ongoing events at a specific point in time, both parameters typically represent the same moment (e.g., "now"), so names like beforeDate and afterDate or simply date for both might better convey the intent. That said, the current implementation is functional and the comment provides adequate guidance.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfaa6fd and ed36952.

📒 Files selected for processing (4)
  • src/main/java/exercise8/controller/api/AllergenRestController.java (1 hunks)
  • src/main/java/exercise8/entity/Allergen.java (1 hunks)
  • src/main/java/exercise8/repository/EventRepository.java (1 hunks)
  • src/main/java/exercise8/service/RegistrationService.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/exercise8/service/RegistrationService.java
🧰 Additional context used
🧠 Learnings (1)
📓 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.
🧬 Code graph analysis (2)
src/main/java/exercise8/entity/Allergen.java (1)
src/main/java/exercise8/entity/Participant.java (1)
  • Entity (10-131)
src/main/java/exercise8/controller/api/AllergenRestController.java (3)
src/main/java/exercise8/controller/api/PatrolRestController.java (1)
  • RestController (13-63)
src/main/java/exercise8/controller/api/ParticipantRestController.java (1)
  • RestController (13-66)
src/main/java/exercise8/controller/api/RegistrationRestController.java (1)
  • RestController (13-86)
🔇 Additional comments (3)
src/main/java/exercise8/entity/Allergen.java (1)

43-54: LGTM! The equals() and hashCode() implementation correctly addresses Set usage.

The implementation using the business key (name) for equality is appropriate and resolves the previous review concern. Since name has a unique constraint, this approach ensures correct behavior when Allergen entities are used in Set<Allergen> collections within the Participant entity.

src/main/java/exercise8/controller/api/AllergenRestController.java (1)

20-55: LGTM! REST endpoints follow proper conventions.

The CRUD endpoints are well-structured with appropriate HTTP methods, status codes (201 CREATED, 204 NO CONTENT), and validation using @Valid. The implementation aligns with REST best practices.

src/main/java/exercise8/repository/EventRepository.java (1)

1-11: LGTM! Clean repository structure.

The repository interface is correctly structured with appropriate Spring Data JPA annotations and extends the proper base interface.

var report = eventService.getAllergyReportForEvent(id);

StringBuilder csv = new StringBuilder();
csv.append("Allergi,Antal,Efternamn,Förnamn,Patrull,Roll\n");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I love how you can export the summary as a .csv-file! Although, I feel like 'antal' is bit misplaced here, maybe you could consider summarizing the amount of participants with allergies per scout-corps, in separate rows?

Skärmbild från 2025-11-22 14-32-49

Copy link
Copy Markdown

@Deansie Deansie left a comment

Choose a reason for hiding this comment

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

I really appreciate how you turned an assignment into something actually applicable and sought of after in real life, I would really like to see your finished thesis once done!

Well structured and easy to comprehend, well done! Left a nitpick comment regarding the exported .csv-file you perhaps could consider.

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.

3 participants