feat: implement and improve audit logging with user tracking and UI enhancements - #38
Conversation
…nment with clearer values
…ead of object keys
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 26 minutes and 29 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds user-aware audit logging and rendering: new AuditService.log overload that accepts a User, TicketService and AttachmentService emit user-backed audit entries, AuditLog and TicketViewDTO expose formatted createdAt, and the UI template renders structured audit cards with user, action, values, and timestamp. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as "UI (view.jte)"
participant TicketService as "TicketService"
participant TicketRepo as "TicketRepository"
participant AttachmentService as "AttachmentService"
participant AuditService as "AuditService"
participant DB as "AuditLog (DB)"
User->>UI: create ticket / update status / upload attachment
UI->>TicketService: createTicket / updateStatus / assign/unassign
TicketService->>TicketRepo: save(ticket)
TicketRepo-->>TicketService: savedTicket
TicketService->>AuditService: log(action, field, old, new, ticket, user)
AuditService->>DB: create & persist AuditLog (action, user, ticket, values)
DB-->>AuditService: persisted
AuditService-->>TicketService: return
User->>UI: upload file
UI->>AttachmentService: uploadToTicket(file, ticket, user)
AttachmentService->>AttachmentService: persist attachment (att)
AttachmentService->>AuditService: log(ATTACHMENT_ADDED, null, null, att.getFileName(), ticket, user)
AuditService->>DB: create & persist AuditLog
DB-->>AuditService: persisted
AuditService-->>AttachmentService: return
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/java/org/example/alfs/services/TicketService.java (1)
361-373:⚠️ Potential issue | 🔴 CriticalCritical: NullPointerException on every unassign.
ticket.setInvestigator(null)runs at line 361, so by the timeticket.getInvestigator().getUsername()is evaluated at line 369,getInvestigator()returnsnulland this dereference throws NPE — failing every unassign operation (and, because the method is@Transactional, rolling back the unassign itself). Capture the username before clearing the reference.🐛 Proposed fix
+ String previousInvestigator = ticket.getInvestigator().getUsername(); + ticket.setInvestigator(null); ticket.setStatus(TicketStatus.OPEN); Ticket savedTicket = ticketRepository.save(ticket); auditService.log( AuditAction.UNASSIGNED, "investigator", - ticket.getInvestigator().getUsername(), + previousInvestigator, null, savedTicket, user );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/alfs/services/TicketService.java` around lines 361 - 373, The code nulls the investigator before logging, causing a NullPointerException when auditService.log tries to call ticket.getInvestigator().getUsername(); fix by capturing the investigator username into a local variable (e.g., String oldInvestigator = ticket.getInvestigator() != null ? ticket.getInvestigator().getUsername() : null) before calling ticket.setInvestigator(null), then pass that local variable to auditService.log (retain use of AuditAction.UNASSIGNED, ticketRepository.save(ticket), and savedTicket for context).
🧹 Nitpick comments (4)
src/main/java/org/example/alfs/services/TicketService.java (1)
45-81: Recommended: makecreateNewTickettransactional and drop theticketId:technical prefix.Two points on this method:
- Unlike
updateTicketStatus/assignInvestigator/unassignInvestigator,createNewTicketis not annotated@Transactional.ticketRepository.save(...)andauditService.log(...)therefore run in separate transactions, so an audit failure leaves a ticket persisted without a matchingCREATEDaudit row (and vice-versa for orphan audit rows would not happen here). Wrapping the method in@Transactionalkeeps the two writes atomic and consistent with the rest of the service.- The PR objective explicitly calls out "replacing technical values (e.g., objectKey) with user-friendly data", yet
newValuehere is"ticketId:" + saved.getId(). Consider using the ticket title (or leavingnewValuenullsinceCREATEDdoesn't really have a before/after value) to stay consistent with the user-friendly rendering goal.♻️ Proposed refactor
- //createNewTicket + //createNewTicket + `@Transactional` public TicketViewDTO createNewTicket(TicketCreateDTO dto) { @@ auditService.log( AuditAction.CREATED, - "ticket", + "title", null, - "ticketId:" + saved.getId(), + saved.getTitle(), saved, user );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/alfs/services/TicketService.java` around lines 45 - 81, Mark createNewTicket as `@Transactional` so ticketRepository.save(...) and auditService.log(...) run in the same transaction; then change the auditService.log(...) call to avoid the technical "ticketId:<id>" newValue—use a user-friendly value such as saved.getTitle() (or null) instead of the "ticketId:" prefix so the CREATED audit row contains readable information consistent with other service methods.src/main/java/org/example/alfs/services/AuditService.java (1)
20-42: Recommended: collapse the two overloads to avoid duplication.The old 5-arg
log(...)is duplicated boilerplate. Per the context snippet note for this PR, all current callers (TicketService,AttachmentService) already use the 6-arg overload, so the old one is effectively unused. Either delegate to the new method (passingnullforuser) or remove it outright so future callers don't silently drop the acting user.♻️ Proposed refactor
- // keeping old for safety public void log(AuditAction action, String fieldName, String oldValue, String newValue, Ticket ticket) { - AuditLog log = new AuditLog(); - log.setAction(action); - log.setFieldName(fieldName); - log.setOldValue(oldValue); - log.setNewValue(newValue); - log.setTicket(ticket); - // createdAt sätts automatiskt via `@PrePersist` i AuditLog - auditLogRepository.save(log); + log(action, fieldName, oldValue, newValue, ticket, null); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/alfs/services/AuditService.java` around lines 20 - 42, Collapse the duplicate log overloads in AuditService by removing the 5-arg log(AuditAction, String, String, String, Ticket) or delegating it to the 6-arg log(...) that accepts User; locate the two methods named log in AuditService and either delete the older 5-arg version or replace its body with a single call to log(action, fieldName, oldValue, newValue, ticket, null) so all audit writes go through the AuditLog creation path that sets user and persists via auditLogRepository.save(log); ensure callers (TicketService, AttachmentService) keep using the 6-arg signature or are updated accordingly.src/main/jte/view.jte (1)
309-319: Optional: compare enum values directly instead of viatoString().equals(...).
log.getAction().toString().equals("...")branching is fragile (silent drift if an enum constant is renamed — the compiler won't catch it). JTE supports direct enum references, so prefer identity comparison againstAuditActionconstants, which is both cheaper and refactor-safe.♻️ Proposed refactor
- `@if`(log.getAction().toString().equals("ATTACHMENT_ADDED")) + `@if`(log.getAction() == org.example.alfs.enums.AuditAction.ATTACHMENT_ADDED) File uploaded - `@elseif`(log.getAction().toString().equals("STATUS_CHANGED")) + `@elseif`(log.getAction() == org.example.alfs.enums.AuditAction.STATUS_CHANGED) Status updated - `@elseif`(log.getAction().toString().equals("ASSIGNED")) + `@elseif`(log.getAction() == org.example.alfs.enums.AuditAction.ASSIGNED) Investigator assigned - `@elseif`(log.getAction().toString().equals("CREATED")) + `@elseif`(log.getAction() == org.example.alfs.enums.AuditAction.CREATED) Ticket created `@else` - ${log.getAction().toString().replace("_", " ")} + ${log.getAction().name().replace("_", " ")} `@endif`Note:
UNASSIGNEDandCOMMENT_ADDEDfall through to the@else, rendering as e.g. "UNASSIGNED" — you may want to add explicit branches for them too.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/jte/view.jte` around lines 309 - 319, Replace fragile string comparisons using log.getAction().toString().equals("...") with direct enum identity checks against the AuditAction constants (e.g., log.getAction() == AuditAction.ATTACHMENT_ADDED) inside the template; update the conditional chain in the view to use these enum references for ATTACHMENT_ADDED, STATUS_CHANGED, ASSIGNED, CREATED and consider adding explicit branches for UNASSIGNED and COMMENT_ADDED so they don't fall through to the generic `@else` rendering. Ensure you import or reference the AuditAction enum in the template context if needed so the template can resolve AuditAction constants.src/main/java/org/example/alfs/entities/AuditLog.java (1)
55-60: Optional: extract formatter as a static constant with an explicit locale.Each call instantiates a new
DateTimeFormatter; hoisting it to aprivate static finalfield avoids the allocation and is idiomatic. Also,MMMis locale-sensitive and currently falls back to the JVM default locale, which can produce inconsistent month abbreviations across environments. Consider pinning a locale (e.g.,Locale.ENGLISH) to keep the rendered timestamp stable.♻️ Proposed refactor
+import java.time.format.DateTimeFormatter; +import java.util.Locale; @@ public class AuditLog { + + private static final DateTimeFormatter DISPLAY_FORMATTER = + DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm", Locale.ENGLISH); @@ public String getFormattedCreatedAt() { if (createdAt == null) return ""; - return createdAt.format( - java.time.format.DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm") - ); + return createdAt.format(DISPLAY_FORMATTER); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/alfs/entities/AuditLog.java` around lines 55 - 60, The getFormattedCreatedAt method currently creates a new DateTimeFormatter on each call and relies on the JVM default locale; extract the formatter into a private static final field (e.g., CREATED_AT_FORMATTER) using DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm", Locale.ENGLISH) and update AuditLog.getFormattedCreatedAt to use that constant when formatting createdAt to avoid repeated allocations and ensure stable month abbreviations across environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/main/java/org/example/alfs/services/TicketService.java`:
- Around line 361-373: The code nulls the investigator before logging, causing a
NullPointerException when auditService.log tries to call
ticket.getInvestigator().getUsername(); fix by capturing the investigator
username into a local variable (e.g., String oldInvestigator =
ticket.getInvestigator() != null ? ticket.getInvestigator().getUsername() :
null) before calling ticket.setInvestigator(null), then pass that local variable
to auditService.log (retain use of AuditAction.UNASSIGNED,
ticketRepository.save(ticket), and savedTicket for context).
---
Nitpick comments:
In `@src/main/java/org/example/alfs/entities/AuditLog.java`:
- Around line 55-60: The getFormattedCreatedAt method currently creates a new
DateTimeFormatter on each call and relies on the JVM default locale; extract the
formatter into a private static final field (e.g., CREATED_AT_FORMATTER) using
DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm", Locale.ENGLISH) and update
AuditLog.getFormattedCreatedAt to use that constant when formatting createdAt to
avoid repeated allocations and ensure stable month abbreviations across
environments.
In `@src/main/java/org/example/alfs/services/AuditService.java`:
- Around line 20-42: Collapse the duplicate log overloads in AuditService by
removing the 5-arg log(AuditAction, String, String, String, Ticket) or
delegating it to the 6-arg log(...) that accepts User; locate the two methods
named log in AuditService and either delete the older 5-arg version or replace
its body with a single call to log(action, fieldName, oldValue, newValue,
ticket, null) so all audit writes go through the AuditLog creation path that
sets user and persists via auditLogRepository.save(log); ensure callers
(TicketService, AttachmentService) keep using the 6-arg signature or are updated
accordingly.
In `@src/main/java/org/example/alfs/services/TicketService.java`:
- Around line 45-81: Mark createNewTicket as `@Transactional` so
ticketRepository.save(...) and auditService.log(...) run in the same
transaction; then change the auditService.log(...) call to avoid the technical
"ticketId:<id>" newValue—use a user-friendly value such as saved.getTitle() (or
null) instead of the "ticketId:" prefix so the CREATED audit row contains
readable information consistent with other service methods.
In `@src/main/jte/view.jte`:
- Around line 309-319: Replace fragile string comparisons using
log.getAction().toString().equals("...") with direct enum identity checks
against the AuditAction constants (e.g., log.getAction() ==
AuditAction.ATTACHMENT_ADDED) inside the template; update the conditional chain
in the view to use these enum references for ATTACHMENT_ADDED, STATUS_CHANGED,
ASSIGNED, CREATED and consider adding explicit branches for UNASSIGNED and
COMMENT_ADDED so they don't fall through to the generic `@else` rendering. Ensure
you import or reference the AuditAction enum in the template context if needed
so the template can resolve AuditAction constants.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9dfec583-2fce-4abd-8e45-8622d60c8857
📒 Files selected for processing (5)
src/main/java/org/example/alfs/entities/AuditLog.javasrc/main/java/org/example/alfs/services/AttachmentService.javasrc/main/java/org/example/alfs/services/AuditService.javasrc/main/java/org/example/alfs/services/TicketService.javasrc/main/jte/view.jte
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/main/java/org/example/alfs/services/TicketService.java (2)
324-336:⚠️ Potential issue | 🟠 MajorAudit the implicit status transitions during assignment changes.
Assigning an investigator also changes status
OPEN -> IN_PROGRESS, and unassigning changesIN_PROGRESS -> OPEN, but the audit trail only records the investigator field. This leaves user-visible status changes missing from the log.📝 Proposed fix
+ TicketStatus oldStatus = ticket.getStatus(); ticket.setInvestigator(investigator); ticket.setStatus(TicketStatus.IN_PROGRESS); Ticket savedTicket = ticketRepository.save(ticket); auditService.log( AuditAction.ASSIGNED, "investigator", null, investigator.getUsername(), savedTicket, user ); + auditService.log( + AuditAction.STATUS_CHANGED, + "status", + oldStatus.name(), + TicketStatus.IN_PROGRESS.name(), + savedTicket, + user + );+ TicketStatus oldStatus = ticket.getStatus(); String oldInvestigator = ticket.getInvestigator().getUsername(); ticket.setInvestigator(null); ticket.setStatus(TicketStatus.OPEN); Ticket savedTicket = ticketRepository.save(ticket); auditService.log( AuditAction.UNASSIGNED, "investigator", oldInvestigator, null, savedTicket, user ); + auditService.log( + AuditAction.STATUS_CHANGED, + "status", + oldStatus.name(), + TicketStatus.OPEN.name(), + savedTicket, + user + );Also applies to: 361-375
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/alfs/services/TicketService.java` around lines 324 - 336, The audit currently only logs the investigator change in TicketService when assigning/unassigning (see ticket.setInvestigator and ticket.setStatus followed by auditService.log); update the audit entries to also record the implicit status transition by capturing the previous status (ticket.getStatus() before mutation) and the new status (TicketStatus.IN_PROGRESS or TicketStatus.OPEN) and include them in the auditService.log payload (e.g., as an additional field or a separate audit entry) so both investigator and status changes are auditable; apply the same change to the corresponding unassign branch (the other block around the unassign logic referenced in the review).
45-72:⚠️ Potential issue | 🟠 MajorAdd
@Transactionalto make ticket creation and audit logging atomic.Line 63 persists the ticket, then line 65 logs the audit entry via
auditService.log(), which immediately callsauditLogRepository.save(). Without a transaction boundary on this method, if the audit save fails, the ticket remains persisted—leaving it without its required audit trail. The other audit-related lifecycle methods (updateTicketStatus,assignInvestigator,unassignInvestigator) are all annotated with@Transactional.Suggested fix
+ `@Transactional` public TicketViewDTO createNewTicket(TicketCreateDTO dto) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/example/alfs/services/TicketService.java` around lines 45 - 72, Annotate the createNewTicket method in TicketService with `@Transactional` so ticketRepository.save(...) and the subsequent auditService.log(...) (which calls auditLogRepository.save()) run in the same transaction; update the method signature to include the annotation on createNewTicket(TicketCreateDTO dto) and ensure the class imports org.springframework.transaction.annotation.Transactional (or the project’s transaction annotation) so a failure in auditService.log will roll back the ticket persist.
🧹 Nitpick comments (1)
src/test/java/org/example/alfs/services/TicketServiceTest.java (1)
44-45: Assert the new audit side effects, not just the injected mock.The mock lets the service instantiate, but none of the lifecycle tests verify
AuditAction, field values, ticket, or acting user. A regression that removes or mislabels audit logging would still pass.🧪 Example assertions to add in lifecycle tests
+ verify(auditService).log( + eq(AuditAction.CREATED), + eq("ticket"), + isNull(), + startsWith("ticketId:"), + any(Ticket.class), + eq(reporter) + );+ verify(auditService).log( + eq(AuditAction.STATUS_CHANGED), + eq("status"), + eq(TicketStatus.OPEN.name()), + eq(TicketStatus.IN_PROGRESS.name()), + eq(ticket), + eq(investigator) + );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/java/org/example/alfs/services/TicketServiceTest.java` around lines 44 - 45, The tests currently only inject a mock AuditService (AuditService) but don't assert its side effects; update the lifecycle tests in TicketServiceTest to capture and assert the AuditAction details: use an ArgumentCaptor<AuditAction> (or Mockito.verify with argThat) for auditService.record/save (whichever method is called), verify it was invoked the expected number of times, and assert captured AuditAction fields (action type, field values, the associated Ticket instance, and acting user/id) match the expected lifecycle step; ensure each lifecycle test (create/update/close) includes these assertions so regressions to audit logging are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/main/java/org/example/alfs/services/TicketService.java`:
- Around line 324-336: The audit currently only logs the investigator change in
TicketService when assigning/unassigning (see ticket.setInvestigator and
ticket.setStatus followed by auditService.log); update the audit entries to also
record the implicit status transition by capturing the previous status
(ticket.getStatus() before mutation) and the new status
(TicketStatus.IN_PROGRESS or TicketStatus.OPEN) and include them in the
auditService.log payload (e.g., as an additional field or a separate audit
entry) so both investigator and status changes are auditable; apply the same
change to the corresponding unassign branch (the other block around the unassign
logic referenced in the review).
- Around line 45-72: Annotate the createNewTicket method in TicketService with
`@Transactional` so ticketRepository.save(...) and the subsequent
auditService.log(...) (which calls auditLogRepository.save()) run in the same
transaction; update the method signature to include the annotation on
createNewTicket(TicketCreateDTO dto) and ensure the class imports
org.springframework.transaction.annotation.Transactional (or the project’s
transaction annotation) so a failure in auditService.log will roll back the
ticket persist.
---
Nitpick comments:
In `@src/test/java/org/example/alfs/services/TicketServiceTest.java`:
- Around line 44-45: The tests currently only inject a mock AuditService
(AuditService) but don't assert its side effects; update the lifecycle tests in
TicketServiceTest to capture and assert the AuditAction details: use an
ArgumentCaptor<AuditAction> (or Mockito.verify with argThat) for
auditService.record/save (whichever method is called), verify it was invoked the
expected number of times, and assert captured AuditAction fields (action type,
field values, the associated Ticket instance, and acting user/id) match the
expected lifecycle step; ensure each lifecycle test (create/update/close)
includes these assertions so regressions to audit logging are caught.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8df01335-77ec-4dba-ac37-30ecad55d66c
📒 Files selected for processing (2)
src/main/java/org/example/alfs/services/TicketService.javasrc/test/java/org/example/alfs/services/TicketServiceTest.java
…ransactional support
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/test/java/org/example/alfs/services/TicketServiceTest.java (1)
776-807:⚠️ Potential issue | 🟡 MinorStrengthen the audit verification: the previous-investigator username is never actually checked.
investigatorUser()doesn't set a username, soticket.getInvestigator().getUsername()— the whole point of theoldValuecaptured byunassignInvestigator— isnullin this test. Theany()matcher at position 3 happily accepts that null, so the test currently passes even if the service had a bug that forgot to capture the previous username (e.g., reordered the statements and read the username aftersetInvestigator(null), which would NPE, or if it passed an empty string). Set a real username on the investigator and assert it explicitly.🧪 Suggested stronger assertion
User investigator = investigatorUser(); + investigator.setUsername("inv-user"); ticket.setInvestigator(investigator); ticket.setStatus(TicketStatus.IN_PROGRESS); @@ verify(auditService).log( eq(AuditAction.UNASSIGNED), eq("investigator"), - any(), + eq("inv-user"), isNull(), any(), eq(admin) );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/java/org/example/alfs/services/TicketServiceTest.java` around lines 776 - 807, Test currently doesn't verify the previous-investigator username captured in the audit log; set a concrete username on the investigator and assert it explicitly. In the test method unassignInvestigator_shouldSucceed, after creating User investigator = investigatorUser() set investigator.setUsername("investigator1") (or update investigatorUser() to return a user with a non-null username), then change the auditService.log verifier's third argument from any() to eq("investigator1") (keeping the other matchers the same) so the test asserts TicketService.unassignInvestigator actually passes the prior username as oldValue when calling auditService.log with AuditAction.UNASSIGNED.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/test/java/org/example/alfs/services/TicketServiceTest.java`:
- Around line 776-807: Test currently doesn't verify the previous-investigator
username captured in the audit log; set a concrete username on the investigator
and assert it explicitly. In the test method unassignInvestigator_shouldSucceed,
after creating User investigator = investigatorUser() set
investigator.setUsername("investigator1") (or update investigatorUser() to
return a user with a non-null username), then change the auditService.log
verifier's third argument from any() to eq("investigator1") (keeping the other
matchers the same) so the test asserts TicketService.unassignInvestigator
actually passes the prior username as oldValue when calling auditService.log
with AuditAction.UNASSIGNED.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 620e4585-9e8b-4d1c-9b7d-1ac7fcf92a38
📒 Files selected for processing (4)
src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.javasrc/main/java/org/example/alfs/services/TicketService.javasrc/main/jte/view.jtesrc/test/java/org/example/alfs/services/TicketServiceTest.java
✅ Files skipped from review due to trivial changes (1)
- src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/jte/view.jte
|
The nitpicks is fixed. |
simonforsberg
left a comment
There was a problem hiding this comment.
Highly approved! Mighty fine work.
This PR completes and improves the audit logging functionality across the application.
What was added/changed:
Result:
Audit logs are now significantly more user-friendly and provide a clear overview of system activity (Who, What, When), instead of raw technical data.
Summary by CodeRabbit