Skip to content

Feature/view incident#71

Merged
HerrKanin merged 9 commits into
mainfrom
feature/view-incident
Apr 8, 2026
Merged

Feature/view incident#71
HerrKanin merged 9 commits into
mainfrom
feature/view-incident

Conversation

@HerrKanin

@HerrKanin HerrKanin commented Apr 8, 2026

Copy link
Copy Markdown
Collaborator

Added view commetns for a specific incident, add a new comment directly the incident page, see who wrote the comment and when it was created

Summary by CodeRabbit

  • New Features

    • New incident detail page showing incident info, comments panel, comment form, and Back link.
    • "View" button on listings navigates to the incident detail page.
  • Behavior Changes

    • After submitting a comment, users are redirected to the incident detail view.
    • Listings now escape/interpolate text safely instead of rendering rich HTML.
    • Detail page loads incident and comments client-side and shows formatted timestamps; detail URLs use /incidents/{id}.

@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Converted comment creation to an MVC redirect, renamed Comment timestamp/lifecycle fields, added a secured GET /api/incidents/{id} with RBAC in the service, replaced a server-rendered incident view with a client-driven /incidents/{id} page, and added a new viewincident.html that fetches incident and comment data client-side.

Changes

Cohort / File(s) Summary
Comment Controller
src/main/java/org/example/team6backend/comment/controller/CommentController.java
Class changed from @RestController@Controller. GET /comments/incident/{incidentId} now has @ResponseBody. POST /comments signature changed from returning ResponseEntity<Comment> to returning a String redirect to /incidents/{incidentId} and expects form-bound params.
Comment Entity
src/main/java/org/example/team6backend/comment/entity/Comment.java
Field renamed createAtcreatedAt with @Column(name = "created_at"); lifecycle callback renamed onCreate()onCreated(); assignment updated to createdAt.
Incident API & Service
src/main/java/org/example/team6backend/incident/controller/IncidentController.java, src/main/java/org/example/team6backend/incident/service/IncidentService.java
Added GET /api/incidents/{id} endpoint. IncidentService.findById(...) replaced by getById(Long id, AppUser user) which throws when not found and enforces access: ADMIN, assigned handler, or creator; otherwise returns 403.
Page Controller (view route)
src/main/java/org/example/team6backend/page/PageController.java
Replaced server-side GET /incident/{id} that loaded the Incident with GET /incidents/{id} which does not load the entity but injects role, incidentId, and userId into the model and returns the viewincident template.
Frontend templates
src/main/resources/templates/incidents.html, src/main/resources/templates/viewincident.html
incidents.html adds a "View" button and viewIncident(id) navigation, plus escapeHtml helper used in rendering. New viewincident.html template fetches /api/incidents/{id} and /comments/incident/{id}, renders incident details and comments client-side, populates hidden incidentId in the comment form, and includes basic error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client (Browser)
    participant PageCtrl as PageController (MVC)
    participant IncidentCtrl as IncidentController (REST)
    participant IncidentSvc as IncidentService (Service)
    participant Repo as IncidentRepository (DB)
    participant CommentCtrl as CommentController (Controller)

    Browser->>PageCtrl: GET /incidents/{id}
    PageCtrl-->>Browser: viewincident.html (model: incidentId, role, userId)

    Browser->>IncidentCtrl: GET /api/incidents/{id}
    IncidentCtrl->>IncidentSvc: getById(id, user)
    IncidentSvc->>Repo: findById(id)
    Repo-->>IncidentSvc: Incident or not found
    IncidentSvc-->>IncidentCtrl: IncidentResponse or throws (404/403)
    IncidentCtrl-->>Browser: 200 OK JSON or 4xx

    Browser->>CommentCtrl: GET /comments/incident/{id}
    CommentCtrl-->>Browser: 200 OK List<Comment> JSON

    Browser->>CommentCtrl: POST /comments (form submit)
    CommentCtrl-->>Browser: Redirect 302 -> /incidents/{incidentId}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through code and left a trace,
Timestamps tidied, views found their place.
I nudged a route, then sent a ping,
Comments now bounce and pages sing.
A tiny hop — behold the spring!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/view incident' is partially related to the changeset. While viewing incidents is a key component, the PR also adds comment viewing/creation, controller refactoring, and entity changes that represent significant portions of the work.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/view-incident

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: 3

🧹 Nitpick comments (4)
src/main/resources/templates/incidents.html (1)

148-148: Empty div serves no purpose.

This flex container div is empty and doesn't contribute to the layout. It appears to be leftover from restructuring or an incomplete implementation.

🧹 Remove empty div
 <div class="incident-footer">
     <span>ID: ${incident.id}</span>
-    <div style="display: flex; gap: 0.5rem;"></div>
     <button class="btn" onclick="viewIncident(${incident.id})">View</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/incidents.html` at line 148, Remove the empty
flex container div (<div style="display: flex; gap: 0.5rem;"></div>) from the
incidents.html template since it serves no purpose; simply delete that element
so the DOM has no unused empty container left behind.
src/main/java/org/example/team6backend/page/PageController.java (1)

73-80: Consider validating incident existence server-side.

The endpoint renders the view without verifying the incident exists. While the template handles API errors gracefully on the client side, a server-side check would provide a better UX (e.g., redirect to /incidents with an error message) instead of rendering a page that shows "Failed to load incident."

Also note that unlike /create-incident and /admin, this endpoint has no role-based restriction. If this is intentional (all authenticated users can view any incident), it's fine—just confirming the design choice.

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

In `@src/main/java/org/example/team6backend/page/PageController.java` around lines
73 - 80, The viewIncident method in PageController renders the view without
verifying the incident exists; inject and use the existing IncidentService or
IncidentRepository to check presence inside viewIncident(Long id, ...), and if
the incident is missing redirect to "/incidents" with a flash attribute or model
error message (e.g., addRedirectAttribute("error","Incident not found")) instead
of returning "viewincident". Also consider adding role-based access if desired
by annotating viewIncident with a security annotation (e.g., `@PreAuthorize` or
`@Secured`) or otherwise documenting that all authenticated users may view
incidents.
src/main/resources/templates/viewincident.html (2)

140-152: Use server-provided incidentId instead of URL parsing.

The controller already passes incidentId to the model, but the form populates it via JavaScript from the URL. Use Thymeleaf directly for consistency and to avoid potential mismatch if the URL is manipulated:

♻️ Suggested change
-            <input type="hidden" name="incidentId" id="incidentIdInput"/>
+            <input type="hidden" name="incidentId" th:value="${incidentId}"/>

This also makes setIncidentIdInForm() unnecessary.

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

In `@src/main/resources/templates/viewincident.html` around lines 140 - 152, The
form currently fills incidentId via client-side URL parsing (and makes
setIncidentIdInForm() unnecessary); instead bind the server-provided model value
directly: set the hidden input with id "incidentIdInput" to use Thymeleaf value
binding (th:value="${incidentId}") and remove the JS function
setIncidentIdInForm() and any script that parses the URL for incidentId so the
form uses the controller-provided incidentId consistently.

197-200: Minor: Consider escaping incident.id for consistency.

While incident.id is expected to be numeric from the API, applying escapeHtml to all dynamic values maintains consistent defense-in-depth:

♻️ Suggested change
                     <div class="incident-footer">
-                        <span>ID: ${incident.id}</span>
+                        <span>ID: ${escapeHtml(incident.id)}</span>
                         <span>Status: ${escapeHtml(incident.incidentStatus || "N/A")}</span>
                     </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/templates/viewincident.html` around lines 197 - 200, The
ID span currently outputs ${incident.id} unescaped; update the incident-footer
rendering to pass the ID through the existing escapeHtml helper (ensuring you
convert the numeric id to a string if needed) so the span uses
escapeHtml(incident.id) for consistent escaping with the other fields.
🤖 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/comment/controller/CommentController.java`:
- Around line 30-34: The createComment method should handle validation failures
and guard against a null incidentId: change the signature of
CommentController.createComment to accept a
org.springframework.validation.BindingResult immediately after the `@Valid`
CommentRequest, check bindingResult.hasErrors() and return the appropriate view
(e.g., the incident detail page or the form view) so validation errors render
instead of throwing MethodArgumentNotValidException; also
validate/request.getIncidentId() != null before building the redirect, and if
null, handle gracefully (render an error view or redirect to a safe list page)
instead of concatenating "null"; ensure you still call
commentService.createComment(request.getIncidentId(), request.getUserId(),
request.getMessage()) only when validation passes and incidentId is present.

In
`@src/main/java/org/example/team6backend/incident/service/IncidentService.java`:
- Around line 71-73: Replace the generic RuntimeException thrown from
IncidentService.getById with a specific 404-mapped exception: create
IncidentNotFoundException extends RuntimeException and throw it from
IncidentService.getById when incidentRepository.findById(id) is empty, or
alternatively throw a ResponseStatusException(HttpStatus.NOT_FOUND, "...") from
getById; then add or update a `@ControllerAdvice` (e.g., GlobalExceptionHandler)
with an `@ExceptionHandler`(IncidentNotFoundException.class) that returns
ResponseEntity.status(HttpStatus.NOT_FOUND) so the service returns HTTP 404 for
missing incidents.

In `@src/main/resources/templates/incidents.html`:
- Around line 136-144: The template uses card.innerHTML to inject unescaped user
fields (incident.subject, incident.incidentCategory, incident.description),
creating an XSS risk; fix by avoiding direct innerHTML: build DOM nodes with
document.createElement and set textContent for title/category/description or
implement and call an escapeHtml(text) helper before inserting into the
template, replacing the inline `${...}` insertions; update the code that
populates the card (reference card.innerHTML usage) to use safe DOM manipulation
or escaped strings for all three fields.

---

Nitpick comments:
In `@src/main/java/org/example/team6backend/page/PageController.java`:
- Around line 73-80: The viewIncident method in PageController renders the view
without verifying the incident exists; inject and use the existing
IncidentService or IncidentRepository to check presence inside viewIncident(Long
id, ...), and if the incident is missing redirect to "/incidents" with a flash
attribute or model error message (e.g., addRedirectAttribute("error","Incident
not found")) instead of returning "viewincident". Also consider adding
role-based access if desired by annotating viewIncident with a security
annotation (e.g., `@PreAuthorize` or `@Secured`) or otherwise documenting that all
authenticated users may view incidents.

In `@src/main/resources/templates/incidents.html`:
- Line 148: Remove the empty flex container div (<div style="display: flex; gap:
0.5rem;"></div>) from the incidents.html template since it serves no purpose;
simply delete that element so the DOM has no unused empty container left behind.

In `@src/main/resources/templates/viewincident.html`:
- Around line 140-152: The form currently fills incidentId via client-side URL
parsing (and makes setIncidentIdInForm() unnecessary); instead bind the
server-provided model value directly: set the hidden input with id
"incidentIdInput" to use Thymeleaf value binding (th:value="${incidentId}") and
remove the JS function setIncidentIdInForm() and any script that parses the URL
for incidentId so the form uses the controller-provided incidentId consistently.
- Around line 197-200: The ID span currently outputs ${incident.id} unescaped;
update the incident-footer rendering to pass the ID through the existing
escapeHtml helper (ensuring you convert the numeric id to a string if needed) so
the span uses escapeHtml(incident.id) for consistent escaping with the other
fields.
🪄 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: 7c60184a-93bb-40d6-84d2-4b33a46df8df

📥 Commits

Reviewing files that changed from the base of the PR and between 85ffdd0 and 414a62a.

📒 Files selected for processing (7)
  • src/main/java/org/example/team6backend/comment/controller/CommentController.java
  • src/main/java/org/example/team6backend/comment/entity/Comment.java
  • src/main/java/org/example/team6backend/incident/controller/IncidentController.java
  • src/main/java/org/example/team6backend/incident/service/IncidentService.java
  • src/main/java/org/example/team6backend/page/PageController.java
  • src/main/resources/templates/incidents.html
  • src/main/resources/templates/viewincident.html

Comment on lines 30 to 34
@PostMapping
public ResponseEntity<Comment> createComment(@Valid @RequestBody CommentRequest request) {
Comment saveComment = commentService.createComment(request.getIncidentId(), request.getUserId(),
request.getMessage());
return ResponseEntity.ok(saveComment);
public String createComment(@Valid CommentRequest request) {
commentService.createComment(request.getIncidentId(), request.getUserId(), request.getMessage());
return "redirect:/incidents/" + request.getIncidentId();
}

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 | 🟡 Minor

Handle validation errors and null incidentId gracefully.

Two concerns with the redirect flow:

  1. Validation errors: If @Valid fails (e.g., empty message), Spring throws MethodArgumentNotValidException, likely showing a Whitelabel error page without a global handler.

  2. Null safety: If request.getIncidentId() is null (malformed form submission), the string concatenation on line 33 would produce redirect:/incidents/null.

🛡️ Suggested improvement
 `@PostMapping`
-public String createComment(`@Valid` CommentRequest request) {
+public String createComment(`@Valid` CommentRequest request, BindingResult bindingResult) {
+    if (bindingResult.hasErrors() || request.getIncidentId() == null) {
+        // Redirect back with error indicator, or to incidents list
+        return "redirect:/incidents";
+    }
     commentService.createComment(request.getIncidentId(), request.getUserId(), request.getMessage());
     return "redirect:/incidents/" + request.getIncidentId();
 }

You'd also need to import org.springframework.validation.BindingResult.

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

In
`@src/main/java/org/example/team6backend/comment/controller/CommentController.java`
around lines 30 - 34, The createComment method should handle validation failures
and guard against a null incidentId: change the signature of
CommentController.createComment to accept a
org.springframework.validation.BindingResult immediately after the `@Valid`
CommentRequest, check bindingResult.hasErrors() and return the appropriate view
(e.g., the incident detail page or the form view) so validation errors render
instead of throwing MethodArgumentNotValidException; also
validate/request.getIncidentId() != null before building the redirect, and if
null, handle gracefully (render an error view or redirect to a safe list page)
instead of concatenating "null"; ensure you still call
commentService.createComment(request.getIncidentId(), request.getUserId(),
request.getMessage()) only when validation passes and incidentId is present.

Comment thread src/main/java/org/example/team6backend/incident/service/IncidentService.java Outdated
Comment thread src/main/resources/templates/incidents.html

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

🤖 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/service/IncidentService.java`:
- Around line 63-64: The service removed the original findById API causing
callers to break; restore compatibility by adding a compat shim method named
findById(Long id) in IncidentService that simply delegates to the new
getById(Long id) (and mark it `@Deprecated` if desired) so existing callers
compile, while leaving the new getById implementation using
incidentRepository.findById(...). Ensure the shim throws the same exception
behavior by forwarding the result/exception from getById.

In `@src/main/java/org/example/team6backend/page/PageController.java`:
- Around line 162-169: There are two identical handlers named viewIncident(Long,
CustomUserDetails, Model) causing a duplicate-method compile error; remove one
of the duplicate methods or consolidate them into a single handler, and if both
routes are required keep one method and rename the other (e.g., viewIncidentList
or viewIncidentAlternate) and update its `@GetMapping` accordingly (/incident/{id}
vs /incidents/{id}) so method names and signatures are unique while preserving
the desired route behavior and model attributes (role, incidentId, userId) set
in the surviving handler.
🪄 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: 0253eb11-a181-488a-b3ee-aee7f0f0b65f

📥 Commits

Reviewing files that changed from the base of the PR and between 5366dba and e2bf431.

📒 Files selected for processing (3)
  • src/main/java/org/example/team6backend/incident/controller/IncidentController.java
  • src/main/java/org/example/team6backend/incident/service/IncidentService.java
  • src/main/java/org/example/team6backend/page/PageController.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/example/team6backend/incident/controller/IncidentController.java

Comment thread src/main/java/org/example/team6backend/incident/service/IncidentService.java Outdated

@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

🤖 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/page/PageController.java`:
- Around line 134-141: The viewIncident GET in PageController (viewIncident)
currently always renders the page shell and skips the old per-incident
authorization (admin || creator || assignee), allowing deep-link access; restore
that check in the shared backend path by moving the ownership/assignment logic
into the incident service (e.g., add
IncidentService.isAuthorizedForIncident(User user, Long incidentId) or perform
the check inside IncidentService.getIncident) and call it from both
IncidentController (where /api/incidents/{id} lives) and
PageController.viewIncident so both endpoints enforce the same semantics and
return 403 or 404 as before when unauthorized or not found. Ensure the check
uses the same role/ownership rules (admin || creator || assignee) and that
PageController does not render the page unless the service authorizes the
request.
🪄 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: b7b2893f-6b11-42ff-a000-fa78549d1b7b

📥 Commits

Reviewing files that changed from the base of the PR and between e2bf431 and c096da7.

📒 Files selected for processing (1)
  • src/main/java/org/example/team6backend/page/PageController.java

Comment thread src/main/java/org/example/team6backend/page/PageController.java
@HerrKanin
HerrKanin merged commit e6575cc into main Apr 8, 2026
3 checks passed
@kristinaxm
kristinaxm deleted the feature/view-incident branch April 9, 2026 09:36
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