Skip to content

Add Incident module: entity, repository, service, controller, DTOs; r…#27

Merged
SandraNelj merged 3 commits into
mainfrom
feature/incidententity
Mar 27, 2026
Merged

Add Incident module: entity, repository, service, controller, DTOs; r…#27
SandraNelj merged 3 commits into
mainfrom
feature/incidententity

Conversation

@SandraNelj

@SandraNelj SandraNelj commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

…eorganize packages

Summary by CodeRabbit

  • New Features

    • Added incident management API: create incidents (POST /api/incidents) and list incidents (GET /api/incidents). Responses include id, subject, description, category, status, creator, assignee, and timestamp.
    • Request validation: subject and category are required with clear error messages.
  • Refactor

    • Reorganized package/module structure to separate user and incident concerns for clearer organization and maintainability.

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fa7c82fa-c7b6-47b4-978f-0d531bdc60f3

📥 Commits

Reviewing files that changed from the base of the PR and between 23d6e6f and 4656b55.

📒 Files selected for processing (5)
  • src/main/java/org/example/team6backend/controller/AdminController.java
  • src/main/java/org/example/team6backend/user/controller/UserController.java
  • src/main/java/org/example/team6backend/user/dto/UserResponse.java
  • src/main/java/org/example/team6backend/user/repository/AppUserRepository.java
  • src/main/java/org/example/team6backend/user/service/UserService.java
✅ Files skipped from review due to trivial changes (3)
  • src/main/java/org/example/team6backend/user/dto/UserResponse.java
  • src/main/java/org/example/team6backend/user/controller/UserController.java
  • src/main/java/org/example/team6backend/controller/AdminController.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/example/team6backend/user/service/UserService.java
  • src/main/java/org/example/team6backend/user/repository/AppUserRepository.java

📝 Walkthrough

Walkthrough

Reorganized user-related classes into org.example.team6backend.user.* packages and added a new incident management module: entity, enums, DTOs, repository, service (which reads SecurityContext), and controller exposing POST and GET endpoints.

Changes

Cohort / File(s) Summary
User package reorganization
src/main/java/org/example/team6backend/user/entity/AppUser.java, src/main/java/org/example/team6backend/user/entity/UserRole.java, src/main/java/org/example/team6backend/user/dto/UserResponse.java, src/main/java/org/example/team6backend/user/controller/UserController.java, src/main/java/org/example/team6backend/user/repository/AppUserRepository.java, src/main/java/org/example/team6backend/user/service/UserService.java
Moved user entities, DTO, controller, repository, and service into org.example.team6backend.user.* package hierarchy (package and import updates only).
Import updates (call sites)
src/main/java/org/example/team6backend/controller/AdminController.java, src/main/java/org/example/team6backend/security/CustomOAuth2UserService.java, src/main/java/org/example/team6backend/security/CustomUserDetails.java
Updated imports to reference relocated AppUser, UserRole, and UserService under the user packages; no behavioural changes.
Incident enums
src/main/java/org/example/team6backend/incident/entity/IncidentCategory.java, src/main/java/org/example/team6backend/incident/entity/IncidentStatus.java
Changed package for incident enums from org.example.team6backend.entityorg.example.team6backend.incident.entity.
Incident entity & DTOs
src/main/java/org/example/team6backend/incident/entity/Incident.java, src/main/java/org/example/team6backend/incident/dto/IncidentRequest.java, src/main/java/org/example/team6backend/incident/dto/IncidentResponse.java
Added JPA Incident entity (relations to AppUser, enums, timestamp), request DTO with validation, and response DTO with fromEntity factory mapping nested users to emails.
Incident persistence & service
src/main/java/org/example/team6backend/incident/repository/IncidentRepository.java, src/main/java/org/example/team6backend/incident/service/IncidentService.java
Added Spring Data repository with derived queries (findByCreatedBy, findByAssignedTo) and a service that sets createdBy from SecurityContext, initializes status/createdAt, and saves incidents.
Incident controller
src/main/java/org/example/team6backend/incident/controller/IncidentController.java
Added REST controller /api/incidents with POST to create (validates body, returns IncidentResponse) and GET to list all incidents (maps entities to responses).

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant IncidentController as "IncidentController"
    participant IncidentService as "IncidentService"
    participant SecurityContext as "SecurityContext"
    participant IncidentRepository as "IncidentRepository"
    participant Database as "Database"

    Client->>IncidentController: POST /api/incidents (IncidentRequest)
    IncidentController->>IncidentService: createIncident(Incident)
    IncidentService->>SecurityContext: get Authentication / principal
    SecurityContext-->>IncidentService: CustomUserDetails -> AppUser
    IncidentService->>IncidentService: set createdBy, status=OPEN, createdAt=now
    IncidentService->>IncidentRepository: save(Incident)
    IncidentRepository->>Database: INSERT Incident
    Database-->>IncidentRepository: persisted Incident
    IncidentRepository-->>IncidentService: Incident
    IncidentService-->>IncidentController: Incident
    IncidentController->>Client: IncidentResponse (201)

    Client->>IncidentController: GET /api/incidents
    IncidentController->>IncidentService: findAll()
    IncidentService->>IncidentRepository: findAll()
    IncidentRepository->>Database: SELECT * FROM incidents
    Database-->>IncidentRepository: List<Incident>
    IncidentRepository-->>IncidentService: List<Incident>
    IncidentService-->>IncidentController: List<Incident>
    IncidentController->>Client: List<IncidentResponse> (200)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hop through packages, move DTOs with care,
Enums find new burrows, incidents bloom there.
I fetch the user from a security nest,
Save subjects and statuses, then let POST do the rest.
Hooray — new endpoints, a carrot for the nest! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 PR title accurately describes the main changes: adding an Incident module with entity, repository, service, controller, and DTOs, plus package reorganization.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/incidententity

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (8)
src/main/java/org/example/team6backend/incident/service/IncidentService.java (2)

24-27: Add defensive check for principal type.

While the OAuth2 flow currently guarantees CustomUserDetails, a direct cast without validation can fail if authentication configuration changes (e.g., adding Basic Auth, API keys). Consider a defensive check:

🛡️ Proposed defensive check
     public Incident createIncident(Incident incident){
         Authentication auth = SecurityContextHolder.getContext().getAuthentication();
-        CustomUserDetails userDetails = (CustomUserDetails) auth.getPrincipal();
+        Object principal = auth.getPrincipal();
+        if (!(principal instanceof CustomUserDetails userDetails)) {
+            throw new IllegalStateException("Unexpected principal type: " + principal.getClass());
+        }
         AppUser appUser = userDetails.getUser();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/team6backend/incident/service/IncidentService.java`
around lines 24 - 27, The createIncident method currently casts
Authentication.getPrincipal() directly to CustomUserDetails which can fail if
the principal type changes; update the method to first retrieve Authentication
from SecurityContextHolder, check that auth.getPrincipal() is an instance of
CustomUserDetails (using instanceof), then cast and proceed to get AppUser; if
the principal is not CustomUserDetails, handle gracefully (throw a clear
unchecked exception or return a 401/forbidden result) so createIncident,
Authentication, SecurityContextHolder, CustomUserDetails and AppUser are guarded
against configuration changes.

45-46: Minor formatting inconsistency.

Space between List and <Incident>.

✏️ Fix formatting
-    public List <Incident> findAll(){
+    public List<Incident> findAll() {
         return incidentRepository.findAll();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/team6backend/incident/service/IncidentService.java`
around lines 45 - 46, The method signature in IncidentService.findAll has a
formatting inconsistency: remove the extra space between the generic type and
the raw type so it reads List<Incident> findAll() instead of List <Incident>
findAll(); update the method declaration in the IncidentService class
accordingly (keeping return incidentRepository.findAll() unchanged).
src/main/java/org/example/team6backend/incident/repository/IncidentRepository.java (1)

11-12: Minor formatting inconsistency.

Line 12 has a space between List and <Incident> while line 11 doesn't.

✏️ Fix formatting
     List<Incident> findByCreatedBy(AppUser user);
-    List <Incident> findByAssignedTo(AppUser user);
+    List<Incident> findByAssignedTo(AppUser user);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/example/team6backend/incident/repository/IncidentRepository.java`
around lines 11 - 12, Fix the minor formatting inconsistency in the repository
interface: in the method declaration for findByAssignedTo(AppUser user) (the
symbol to change is the return type on the line containing findByAssignedTo),
remove the unintended space between "List" and "<Incident>" so both method
signatures use the same "List<Incident>" formatting as findByCreatedBy(AppUser
user).
src/main/java/org/example/team6backend/incident/entity/Incident.java (3)

26-27: Consider adding modifiedAt timestamp for audit consistency.

The entity has modifiedBy but lacks a corresponding modifiedAt timestamp. For complete audit trails, both fields are typically tracked together.

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

In `@src/main/java/org/example/team6backend/incident/entity/Incident.java` around
lines 26 - 27, The Incident entity has a modifiedBy field but no modifiedAt
timestamp; add a new field (e.g., private Instant modifiedAt or LocalDateTime
modifiedAt) to the Incident class to record when modifications occur, annotate
it appropriately for persistence (e.g., `@Column` and/or
`@LastModifiedDate/`@Temporal as used elsewhere in the project), update
constructors/getters/setters and any builders or DTO mappers that touch
Incident, and ensure the service/repository layer that sets modifiedBy also sets
modifiedAt so the audit trail remains consistent with the modifiedBy field.

23-30: Consider lazy fetching for @ManyToOne relationships.

The default fetch type for @ManyToOne is EAGER, which loads all three AppUser associations immediately. When fetching lists of incidents (e.g., findAll()), this can result in additional queries per incident.

For list operations, consider using FetchType.LAZY and fetching users only when needed:

♻️ Proposed change
-    `@ManyToOne`
+    `@ManyToOne`(fetch = FetchType.LAZY)
     private AppUser createdBy;
 
-    `@ManyToOne`
+    `@ManyToOne`(fetch = FetchType.LAZY)
     private AppUser modifiedBy;
 
-    `@ManyToOne`
+    `@ManyToOne`(fetch = FetchType.LAZY)
     private AppUser assignedTo;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/team6backend/incident/entity/Incident.java` around
lines 23 - 30, Update the three `@ManyToOne` associations in the Incident entity
(fields createdBy, modifiedBy, assignedTo) to use lazy loading by adding fetch =
FetchType.LAZY to each annotation; ensure you import
javax.persistence.FetchType. Keep business logic that expects user data working
by explicitly fetching users when needed (e.g., use JOIN FETCH in repository
queries or map to DTOs) to avoid LazyInitializationException at runtime.

8-9: Consider using Lombok for consistency with other entities.

AppUser uses Lombok @Data. This entity has manual getters/setters, which adds boilerplate. Consider using Lombok for consistency:

♻️ Proposed change
 package org.example.team6backend.incident.entity;
 
 import jakarta.persistence.*;
+import lombok.Data;
+import lombok.NoArgsConstructor;
 import org.example.team6backend.user.entity.AppUser;
 
 import java.time.LocalDateTime;
 
 `@Entity`
+@Data
+@NoArgsConstructor
 public class Incident {
     `@Id`
     `@GeneratedValue`(strategy = GenerationType.IDENTITY)
     private Long id;
     // ... fields only, remove manual getters/setters
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/team6backend/incident/entity/Incident.java` around
lines 8 - 9, The Incident entity currently defines manual getters/setters while
other entities (e.g., AppUser) use Lombok; replace the boilerplate by adding
Lombok annotations to the Incident class (for example `@Data` plus
`@NoArgsConstructor` and `@AllArgsConstructor` or `@Builder` as needed), add the
corresponding lombok imports, and remove the explicit getter/setter methods so
the class relies on Lombok-generated accessors; ensure the class-level
annotation is placed on class Incident and run a compile to confirm no remaining
manually defined accessors conflict.
src/main/java/org/example/team6backend/incident/controller/IncidentController.java (2)

32-38: Consider adding pagination for scalability.

Returning all incidents without pagination can cause performance issues and high memory consumption as the dataset grows. Spring Data's Pageable support makes this straightforward to implement.

♻️ Proposed pagination approach
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 `@GetMapping`
-public List<IncidentResponse> getAllIncidents() {
-    return incidentService.findAll()
-            .stream()
-            .map(IncidentResponse::fromEntity)
-            .toList();
+public Page<IncidentResponse> getAllIncidents(Pageable pageable) {
+    return incidentService.findAll(pageable)
+            .map(IncidentResponse::fromEntity);
 }

This requires updating IncidentService.findAll() to accept a Pageable parameter and return Page<Incident>.

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

In
`@src/main/java/org/example/team6backend/incident/controller/IncidentController.java`
around lines 32 - 38, Modify getAllIncidents to accept Spring Pageable (or
`@RequestParam` page/size and build one) and call
incidentService.findAll(pageable) instead of the parameterless findAll(); update
IncidentService.findAll(Pageable) to return Page<Incident>, then map the Page
content via IncidentResponse::fromEntity and return a paged response (e.g.,
Page<IncidentResponse> or ResponseEntity<Page<IncidentResponse>>). Ensure you
change the controller return type accordingly and preserve use of
IncidentResponse::fromEntity when converting entities to DTOs.

21-30: Return HTTP 201 for resource creation.

POST endpoints that create resources should return 201 CREATED rather than the default 200 OK. Add @ResponseStatus(HttpStatus.CREATED) to the method.

♻️ Proposed fix
+import org.springframework.http.HttpStatus;
 `@PostMapping`
+@ResponseStatus(HttpStatus.CREATED)
 public IncidentResponse createIncident(`@RequestBody` IncidentRequest incidentRequest) {

Note: The createdBy field is already populated by IncidentService.createIncident(), which retrieves the authenticated user from SecurityContextHolder and assigns it automatically. No validation constraints are currently defined on IncidentRequest, so @Valid is not applicable at this time.

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

In
`@src/main/java/org/example/team6backend/incident/controller/IncidentController.java`
around lines 21 - 30, The createIncident POST handler currently returns 200 OK
by default; update the IncidentController.createIncident method to annotate it
with `@ResponseStatus`(HttpStatus.CREATED) so it returns HTTP 201 on successful
creation, keeping the existing behavior of calling
incidentService.createIncident(incident) and returning
IncidentResponse.fromEntity(saved); no changes to IncidentRequest validation or
createdBy population are required.
🤖 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/team6backend/incident/dto/IncidentRequest.java`:
- Around line 7-12: Add Bean Validation annotations to the IncidentRequest DTO:
mark subject with `@NotBlank` and incidentCategory with `@NotNull` (and description
optionally with `@Size` or `@NotBlank` if required) so null/empty values are
rejected before persistence; update the controller method createIncident in
IncidentController to accept `@Valid` `@RequestBody` IncidentRequest incidentRequest
so validation is triggered; ensure javax.validation (jakarta.validation)
annotations are imported and that the application has validation
starter/dependency enabled.

---

Nitpick comments:
In
`@src/main/java/org/example/team6backend/incident/controller/IncidentController.java`:
- Around line 32-38: Modify getAllIncidents to accept Spring Pageable (or
`@RequestParam` page/size and build one) and call
incidentService.findAll(pageable) instead of the parameterless findAll(); update
IncidentService.findAll(Pageable) to return Page<Incident>, then map the Page
content via IncidentResponse::fromEntity and return a paged response (e.g.,
Page<IncidentResponse> or ResponseEntity<Page<IncidentResponse>>). Ensure you
change the controller return type accordingly and preserve use of
IncidentResponse::fromEntity when converting entities to DTOs.
- Around line 21-30: The createIncident POST handler currently returns 200 OK by
default; update the IncidentController.createIncident method to annotate it with
`@ResponseStatus`(HttpStatus.CREATED) so it returns HTTP 201 on successful
creation, keeping the existing behavior of calling
incidentService.createIncident(incident) and returning
IncidentResponse.fromEntity(saved); no changes to IncidentRequest validation or
createdBy population are required.

In `@src/main/java/org/example/team6backend/incident/entity/Incident.java`:
- Around line 26-27: The Incident entity has a modifiedBy field but no
modifiedAt timestamp; add a new field (e.g., private Instant modifiedAt or
LocalDateTime modifiedAt) to the Incident class to record when modifications
occur, annotate it appropriately for persistence (e.g., `@Column` and/or
`@LastModifiedDate/`@Temporal as used elsewhere in the project), update
constructors/getters/setters and any builders or DTO mappers that touch
Incident, and ensure the service/repository layer that sets modifiedBy also sets
modifiedAt so the audit trail remains consistent with the modifiedBy field.
- Around line 23-30: Update the three `@ManyToOne` associations in the Incident
entity (fields createdBy, modifiedBy, assignedTo) to use lazy loading by adding
fetch = FetchType.LAZY to each annotation; ensure you import
javax.persistence.FetchType. Keep business logic that expects user data working
by explicitly fetching users when needed (e.g., use JOIN FETCH in repository
queries or map to DTOs) to avoid LazyInitializationException at runtime.
- Around line 8-9: The Incident entity currently defines manual getters/setters
while other entities (e.g., AppUser) use Lombok; replace the boilerplate by
adding Lombok annotations to the Incident class (for example `@Data` plus
`@NoArgsConstructor` and `@AllArgsConstructor` or `@Builder` as needed), add the
corresponding lombok imports, and remove the explicit getter/setter methods so
the class relies on Lombok-generated accessors; ensure the class-level
annotation is placed on class Incident and run a compile to confirm no remaining
manually defined accessors conflict.

In
`@src/main/java/org/example/team6backend/incident/repository/IncidentRepository.java`:
- Around line 11-12: Fix the minor formatting inconsistency in the repository
interface: in the method declaration for findByAssignedTo(AppUser user) (the
symbol to change is the return type on the line containing findByAssignedTo),
remove the unintended space between "List" and "<Incident>" so both method
signatures use the same "List<Incident>" formatting as findByCreatedBy(AppUser
user).

In
`@src/main/java/org/example/team6backend/incident/service/IncidentService.java`:
- Around line 24-27: The createIncident method currently casts
Authentication.getPrincipal() directly to CustomUserDetails which can fail if
the principal type changes; update the method to first retrieve Authentication
from SecurityContextHolder, check that auth.getPrincipal() is an instance of
CustomUserDetails (using instanceof), then cast and proceed to get AppUser; if
the principal is not CustomUserDetails, handle gracefully (throw a clear
unchecked exception or return a 401/forbidden result) so createIncident,
Authentication, SecurityContextHolder, CustomUserDetails and AppUser are guarded
against configuration changes.
- Around line 45-46: The method signature in IncidentService.findAll has a
formatting inconsistency: remove the extra space between the generic type and
the raw type so it reads List<Incident> findAll() instead of List <Incident>
findAll(); update the method declaration in the IncidentService class
accordingly (keeping return incidentRepository.findAll() unchanged).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ae23c52e-f608-4a93-85a9-04767ed51d9a

📥 Commits

Reviewing files that changed from the base of the PR and between d425baa and 3cc932d.

📒 Files selected for processing (17)
  • src/main/java/org/example/team6backend/controller/AdminController.java
  • src/main/java/org/example/team6backend/incident/controller/IncidentController.java
  • src/main/java/org/example/team6backend/incident/dto/IncidentRequest.java
  • src/main/java/org/example/team6backend/incident/dto/IncidentResponse.java
  • src/main/java/org/example/team6backend/incident/entity/Incident.java
  • src/main/java/org/example/team6backend/incident/entity/IncidentCategory.java
  • src/main/java/org/example/team6backend/incident/entity/IncidentStatus.java
  • src/main/java/org/example/team6backend/incident/repository/IncidentRepository.java
  • src/main/java/org/example/team6backend/incident/service/IncidentService.java
  • src/main/java/org/example/team6backend/security/CustomOAuth2UserService.java
  • src/main/java/org/example/team6backend/security/CustomUserDetails.java
  • src/main/java/org/example/team6backend/user/controller/UserController.java
  • src/main/java/org/example/team6backend/user/dto/UserResponse.java
  • src/main/java/org/example/team6backend/user/entity/AppUser.java
  • src/main/java/org/example/team6backend/user/entity/UserRole.java
  • src/main/java/org/example/team6backend/user/repository/AppUserRepository.java
  • src/main/java/org/example/team6backend/user/service/UserService.java

# Conflicts:
#	src/main/java/org/example/team6backend/controller/AdminController.java
#	src/main/java/org/example/team6backend/user/repository/AppUserRepository.java
#	src/main/java/org/example/team6backend/user/service/UserService.java
@SandraNelj
SandraNelj merged commit 2d24afb into main Mar 27, 2026
1 check passed
@SandraNelj SandraNelj mentioned this pull request Mar 27, 2026
@kristinaxm
kristinaxm deleted the feature/incidententity branch March 27, 2026 14:20
@coderabbitai coderabbitai Bot mentioned this pull request Mar 30, 2026
@SandraNelj SandraNelj self-assigned this Mar 31, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Apr 1, 2026
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