Skip to content

Feature/comment on ticket#139

Merged
apaegs merged 5 commits into
mainfrom
feature/comment-on-ticket
Apr 27, 2026
Merged

Feature/comment on ticket#139
apaegs merged 5 commits into
mainfrom
feature/comment-on-ticket

Conversation

@apaegs

@apaegs apaegs commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Add comments on tickets with server-side validation and proper error/success flash messages
    • Close-ticket flow updated: expanded authorization and role-aware redirects
  • UI & Style

    • Styled comment area; comments show author and created-at timestamp
    • Activity log shows human-readable action names and "By" user when available
    • Ticket page displays flash messages and form validation feedback

apaegs added 2 commits April 27, 2026 22:04
…amic elements, and refine styling for comment and activity sections
@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@apaegs has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 30 minutes and 17 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 53cfa669-c9f1-42b9-8874-8d1e06e338a3

📥 Commits

Reviewing files that changed from the base of the PR and between ee97db3 and 6a1de48.

📒 Files selected for processing (1)
  • src/main/java/org/example/untitled/usercase/controller/CaseController.java
📝 Walkthrough

Walkthrough

Adds comment creation and display to tickets, passes actor identity through services for audit logging, maps audit user IDs to usernames for UI display, expands close-ticket authorization to handlers/assigned users, updates templates and styling, and adds a display-name helper on the AuditAction enum.

Changes

Cohort / File(s) Summary
Audit enum
src/main/java/org/example/untitled/usercase/AuditAction.java
Added getDisplayName() and a trailing semicolon to provide human-readable labels for audit actions.
Controller: ticket details & comments
src/main/java/org/example/untitled/usercase/controller/CaseController.java
Injected UserService; compute auditUserMap, canClose, canComment; added CreateCommentRequest model; new POST /tickets/{id}/comments handler with validation, permission checks, error re-rendering, and role-based redirects.
Services: comment, case, user lookup
src/main/java/org/example/untitled/usercase/service/CommentService.java, src/main/java/org/example/untitled/usercase/service/CaseService.java, src/main/java/org/example/untitled/user/service/UserService.java
CommentService now depends on UserRepository and requires caller username to resolve author; CaseService.closeTicket accepts acting username; UserService adds findAllByIds(Collection<Long>) returning id→username map.
Templates & static assets
src/main/resources/templates/ticket.html, src/main/resources/static/style.css
Template: flash messages, comment UI with author/timestamp, activity log uses entry.action.displayName and auditUserMap, comment form gated by canComment, close button uses canClose. CSS: styles for comment box, author, text, and section title.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Browser
    participant Controller as CaseController
    participant CommentSvc as CommentService
    participant CaseSvc as CaseService
    participant UserSvc as UserService
    participant AuditSvc as AuditLogService

    Note over Browser,Controller: View ticket details
    Browser->>Controller: GET /tickets/{id}
    Controller->>CaseSvc: fetch ticket, activities
    Controller->>UserSvc: findAllByIds(activityUserIds)
    Controller-->>Browser: render ticket.html (with auditUserMap, canComment, canClose)

    Note over Browser,Controller: Submit comment
    Browser->>Controller: POST /tickets/{id}/comments (comment, user)
    Controller->>CaseSvc: fetch ticket
    Controller->>CommentSvc: createComment(comment, ticket, username)
    CommentSvc->>UserSvc: findByUsername(username)
    CommentSvc->>AuditSvc: log COMMENT_CREATED (with authorId)
    CommentSvc-->>Controller: success
    Controller-->>Browser: redirect to /tickets/{id}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • viktorlindell12
  • Xeutos

Poem

🐰 In burrows of code I hop and write,
New comments bloom and audits light,
User names stitched into each trail,
Handlers hop in to close the tale—
A tiny rabbit squeaks: hooray, delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/comment on ticket' clearly describes the main change: adding commenting functionality to tickets. It directly aligns with the primary features introduced across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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/comment-on-ticket

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.

@apaegs apaegs linked an issue Apr 27, 2026 that may be closed by this pull request

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

🧹 Nitpick comments (3)
src/main/resources/static/style.css (1)

436-454: Unused CSS classes .comment-box and .comment-author.

The template ticket.html renders comments using .ticket-info (instead of .comment-box) for the wrapper and .ticket-title / .assigned (instead of .comment-author) for the author and timestamp. Either drop these rules or update the template to use them so the styling is intentional and consistent with the new "Comments" UI.

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

In `@src/main/resources/static/style.css` around lines 436 - 454, The CSS defines
unused classes .comment-box and .comment-author while the template ticket.html
now uses .ticket-info, .ticket-title and .assigned; either remove the dead rules
or align the template with the CSS—update ticket.html to replace
.ticket-info/.ticket-title/.assigned usages with .comment-box/.comment-author
(and .comment-text if needed) to apply the styling, or delete/merge the
.comment-box and .comment-author rules from src/main/resources/static/style.css
to avoid unused styles.
src/main/java/org/example/untitled/usercase/controller/CaseController.java (1)

147-159: getTicketByID(id) is fetched twice in closeTicket.

Line 147 already loads the ticket into ticket; line 159 re-fetches it for the model. Reuse the already-loaded value to save a DB round trip and keep the two views consistent.

Proposed fix
-        model.addAttribute("ticket", caseService.getTicketByID(id));
+        model.addAttribute("ticket", ticket);
         model.addAttribute("comment", new CreateCommentRequest());
         return "close_ticket";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/untitled/usercase/controller/CaseController.java`
around lines 147 - 159, The method re-fetches the same ticket twice:
CaseController calls caseService.getTicketByID(id) into local variable ticket,
then calls caseService.getTicketByID(id) again when adding to the model; change
the model.addAttribute call to reuse the already-loaded ticket variable (ticket)
instead of calling getTicketByID(id) again so you avoid an extra DB round trip
and keep the same object used for authorization and view rendering.
src/main/java/org/example/untitled/usercase/AuditAction.java (1)

12-15: Use a fixed locale for toLowerCase().

String.toLowerCase() is locale-sensitive (e.g. Turkish locale lowercases I to ı), which can produce unexpected display names depending on the JVM's default locale. Pass Locale.ROOT for predictable, technical-string casing.

Proposed fix
 public String getDisplayName() {
-    String name = this.name().toLowerCase().replace('_', ' ');
+    String name = this.name().toLowerCase(java.util.Locale.ROOT).replace('_', ' ');
     return Character.toUpperCase(name.charAt(0)) + name.substring(1);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/untitled/usercase/AuditAction.java` around lines 12
- 15, AuditAction.getDisplayName() uses locale-sensitive
this.name().toLowerCase(); change it to use a fixed locale by calling
toLowerCase(Locale.ROOT) so casing is deterministic across JVM locales, and add
the necessary import for java.util.Locale; update the method signature in
AuditAction.getDisplayName() to call this.name().toLowerCase(Locale.ROOT) before
replacing '_' and capitalizing the first character.
🤖 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/untitled/usercase/controller/CaseController.java`:
- Around line 273-288: When handling validation failure inside CaseController
(the bindingResult.hasErrors() branch), you forgot to populate the model
attribute "canClose", so the ticket view hides the comment/close UI; compute
canClose once (extract or call the existing helper method that determines close
permission, e.g., a method like canCloseCase(caseEntityDto, currentUser) or
computeCanClose(...)) and add model.addAttribute("canClose", canClose) in that
branch before returning "ticket"; ensure you reuse the same helper used
elsewhere in this controller so the logic is identical.
- Around line 289-297: Replace the broad silent catch in CaseController around
the comment creation block with specific handling and logging: in the try/catch
that calls comment.setCaseId(id), caseService.getTicketByID(id) and
commentService.createComment(comment, ticket) catch specific exceptions (e.g.,
IllegalArgumentException, DataAccessException, ResponseStatusException) and log
the exception e with an appropriate logger at the same level and message style
used in assignTicketForm/updateStatusForm, then set a user-facing flash error
that includes a concise reason when available; ensure you still fall back to a
generic error flash for unknown exceptions but log the full stack trace before
returning the redirect to "redirect:/tickets/" + id.
- Line 292: The addComment call is missing the authenticated user: change the
controller's addComment to pass the `@AuthenticationPrincipal` UserDetails (or
username/User) into commentService.createComment(...) and update the
CommentService.createComment(...) signature to accept that user parameter;
inside createComment, stop using caseEntity.getOwner() to set the comment author
and instead set the author to the passed authenticated user (or resolve the User
entity from the username if necessary) so the comment author reflects who
submitted it rather than the ticket owner.
- Around line 91-97: The current auditUserMap construction uses
userService.findById per AuditLog and will throw
ResponseStatusException(NOT_FOUND) if any user is deleted (causing
showTicketDetails and addComment to 404) and causes N+1 queries; add a
UserRepository/UserService method findAllByIds(Collection<Long>) and implement a
private helper buildAuditUserMap(List<AuditLog> auditLogs) that collects
non-null userIds into a Set, batch-fetches users via
userService.findAllByIds(ids), and returns a Map<Long,String> of id→username
(resolve duplicates with (a,b)->a) so missing users are simply absent; update
both places that build auditUserMap (the code block using userService.findById
and the duplicate in addComment) to call buildAuditUserMap, and ensure the
template falls back to a placeholder like "Unknown user" when auditUserMap lacks
an id.
- Around line 264-298: The addComment endpoint currently allows any
authenticated user to post to any ticket; replicate the same ownership/role
authorization used in showTicketDetails and processCloseTicket by checking
whether the current user is the owner/handler/assigned (or using the same helper
if one exists) before calling commentService.createComment: fetch the current
user via the same mechanism used elsewhere (authentication principal /
userService), check the same predicates (isOwner/isHandler/isAssigned) against
CaseEntityDto from caseService.getTicketByID(id), and if the check fails, stop
processing and return an access-denied response/redirect (or add a flash error)
instead of creating the comment; place this check just before
comment.setCaseId(id)/commentService.createComment and reuse the exact logic or
helper used in showTicketDetails and processCloseTicket to keep behavior
consistent.

In `@src/main/resources/templates/ticket.html`:
- Around line 48-60: The template currently gates the comment form on the
canClose flag which mixes closing and commenting permissions; update the view to
use a separate canComment boolean (instead of canClose) so commenting visibility
is independent, and ensure the controller that renders this template sets
canComment appropriately; also verify and enforce the same permission in
CaseController.addComment (the POST /tickets/{id}/comments handler) so
server-side checks match the new canComment UI flag.
- Around line 33-46: The template lacks any field-level error display for the
comment form, so when addComment re-renders the "ticket" view due to
bindingResult.hasErrors() users see no validation feedback; update the comment
form to bind to the comment model attribute with th:object="${comment}", change
the textarea to use th:field="*{text}" to preserve input, and add validation
output using th:errors="*{text}" (or a conditional block with
`#fields.hasErrors`('text')) adjacent to the textarea so field errors are shown
when bindingResult.hasErrors() triggers.

---

Nitpick comments:
In `@src/main/java/org/example/untitled/usercase/AuditAction.java`:
- Around line 12-15: AuditAction.getDisplayName() uses locale-sensitive
this.name().toLowerCase(); change it to use a fixed locale by calling
toLowerCase(Locale.ROOT) so casing is deterministic across JVM locales, and add
the necessary import for java.util.Locale; update the method signature in
AuditAction.getDisplayName() to call this.name().toLowerCase(Locale.ROOT) before
replacing '_' and capitalizing the first character.

In `@src/main/java/org/example/untitled/usercase/controller/CaseController.java`:
- Around line 147-159: The method re-fetches the same ticket twice:
CaseController calls caseService.getTicketByID(id) into local variable ticket,
then calls caseService.getTicketByID(id) again when adding to the model; change
the model.addAttribute call to reuse the already-loaded ticket variable (ticket)
instead of calling getTicketByID(id) again so you avoid an extra DB round trip
and keep the same object used for authorization and view rendering.

In `@src/main/resources/static/style.css`:
- Around line 436-454: The CSS defines unused classes .comment-box and
.comment-author while the template ticket.html now uses .ticket-info,
.ticket-title and .assigned; either remove the dead rules or align the template
with the CSS—update ticket.html to replace .ticket-info/.ticket-title/.assigned
usages with .comment-box/.comment-author (and .comment-text if needed) to apply
the styling, or delete/merge the .comment-box and .comment-author rules from
src/main/resources/static/style.css to avoid unused styles.
🪄 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: 6eafb455-49eb-4e30-890c-2a3c59c2eb24

📥 Commits

Reviewing files that changed from the base of the PR and between 3943463 and 0729516.

📒 Files selected for processing (4)
  • src/main/java/org/example/untitled/usercase/AuditAction.java
  • src/main/java/org/example/untitled/usercase/controller/CaseController.java
  • src/main/resources/static/style.css
  • src/main/resources/templates/ticket.html

Comment thread src/main/java/org/example/untitled/usercase/controller/CaseController.java Outdated
Comment thread src/main/java/org/example/untitled/usercase/controller/CaseController.java Outdated
Comment thread src/main/resources/templates/ticket.html
Comment thread src/main/resources/templates/ticket.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: 1

🧹 Nitpick comments (1)
src/main/java/org/example/untitled/usercase/controller/CaseController.java (1)

76-86: Recommended: extract the repeated isHandler/isAssigned/isOwner block into a helper.

The same trio of role/relationship checks is duplicated four times (showTicketDetails, closeTicket GET, processCloseTicket, addComment). The next time the auth model changes (e.g., adding canComment ticket-state gating, or a new role) it has to be updated in all four places — easy to miss. Consider a small TicketPermissions record/helper that returns {isOwner, isAssigned, isHandler, canClose, canComment} for (ticket, userDetails).

Also applies to: 143-146, 163-166, 262-265

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

In `@src/main/java/org/example/untitled/usercase/controller/CaseController.java`
around lines 76 - 86, Extract the repeated role/relationship logic in
CaseController into a small helper record/class (e.g., TicketPermissions) that
takes (Case ticket, UserDetails userDetails) and exposes boolean fields/methods
isOwner, isAssigned, isHandler, canClose, canComment (compute canClose using
ticket.status and the three role booleans). Replace the duplicated blocks in the
methods showTicketDetails, closeTicket (GET), processCloseTicket, and addComment
to construct a TicketPermissions instance and consult its fields instead of
duplicating the checks; keep existing semantics (use caseService.isNotOwner(...)
for handler check where appropriate) so behavior remains identical.
🤖 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/untitled/usercase/controller/CaseController.java`:
- Around line 146-147: The ResponseStatusException message in CaseController (in
the branch checking isHandler, isAssigned, and caseService.isNotOwner in the
close/authorization paths) is misleading ("You do not own this ticket"); change
the exception message to a neutral, accurate one such as "You are not allowed to
close this ticket" in both places (the check around the
isHandler/isAssigned/isNotOwner logic and the similar check later at 166-167) so
handlers/assignees who lose access see the correct text.

---

Nitpick comments:
In `@src/main/java/org/example/untitled/usercase/controller/CaseController.java`:
- Around line 76-86: Extract the repeated role/relationship logic in
CaseController into a small helper record/class (e.g., TicketPermissions) that
takes (Case ticket, UserDetails userDetails) and exposes boolean fields/methods
isOwner, isAssigned, isHandler, canClose, canComment (compute canClose using
ticket.status and the three role booleans). Replace the duplicated blocks in the
methods showTicketDetails, closeTicket (GET), processCloseTicket, and addComment
to construct a TicketPermissions instance and consult its fields instead of
duplicating the checks; keep existing semantics (use caseService.isNotOwner(...)
for handler check where appropriate) so behavior remains identical.
🪄 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: 6b63d1b6-44e8-4f45-b7f9-76faf63ac346

📥 Commits

Reviewing files that changed from the base of the PR and between 0729516 and ee97db3.

📒 Files selected for processing (6)
  • src/main/java/org/example/untitled/user/service/UserService.java
  • src/main/java/org/example/untitled/usercase/AuditAction.java
  • src/main/java/org/example/untitled/usercase/controller/CaseController.java
  • src/main/java/org/example/untitled/usercase/service/CaseService.java
  • src/main/java/org/example/untitled/usercase/service/CommentService.java
  • src/main/resources/templates/ticket.html
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/org/example/untitled/usercase/AuditAction.java
  • src/main/resources/templates/ticket.html

Comment on lines 146 to 147
if (!isHandler && !isAssigned && caseService.isNotOwner(ticket, userDetails.getUsername()))
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "You do not own this ticket");

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

Misleading error message on close-auth failure.

The branch is now reached when the user is neither handler-level nor assignee nor owner, but the message still says "You do not own this ticket" — handlers/assignees who lose access (e.g., reassignment) will see incorrect text. Consider "You are not allowed to close this ticket".

Suggested change
-        if (!isHandler && !isAssigned && caseService.isNotOwner(ticket, userDetails.getUsername()))
-            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "You do not own this ticket");
+        if (!isHandler && !isAssigned && caseService.isNotOwner(ticket, userDetails.getUsername()))
+            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "You are not allowed to close this ticket");

Also applies to: 166-167

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

In `@src/main/java/org/example/untitled/usercase/controller/CaseController.java`
around lines 146 - 147, The ResponseStatusException message in CaseController
(in the branch checking isHandler, isAssigned, and caseService.isNotOwner in the
close/authorization paths) is misleading ("You do not own this ticket"); change
the exception message to a neutral, accurate one such as "You are not allowed to
close this ticket" in both places (the check around the
isHandler/isAssigned/isNotOwner logic and the similar check later at 166-167) so
handlers/assignees who lose access see the correct text.

…roved reusability and streamlined controller logic
@apaegs
apaegs merged commit 40742a8 into main Apr 27, 2026
2 checks passed
@apaegs
apaegs deleted the feature/comment-on-ticket branch April 27, 2026 21:52
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.

Comment on a ticket

2 participants