Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/main/java/org/example/alfs/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers("/auth/hash").permitAll()
.requestMatchers("/h2-console/**").permitAll()
.requestMatchers("/", "/startPage").permitAll()
.requestMatchers(HttpMethod.GET,"/tickets/create").permitAll()
.requestMatchers(HttpMethod.POST, "/tickets/create").permitAll()
.requestMatchers(HttpMethod.GET, "/tickets/create").permitAll()
.requestMatchers("/tickets/ticket-created").permitAll()
.requestMatchers("/tickets/token/**").permitAll()
.requestMatchers(HttpMethod.POST, "/tickets/*/comments").permitAll()
.requestMatchers("/error/**").permitAll()

//allow access to endpoints during development
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/example/alfs/dto/ticket/TicketViewDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.example.alfs.enums.TicketStatus;
import java.time.format.DateTimeFormatter;
import java.time.LocalDateTime;
import java.util.Locale;

/*
* DTO returned when retrieving ticket information.
Expand All @@ -26,9 +27,12 @@ public class TicketViewDTO {
private Long assignedInvestigatorId;
private String assignedInvestigatorName;

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(DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm"));
return createdAt.format(DISPLAY_FORMATTER);
}
}
7 changes: 7 additions & 0 deletions src/main/java/org/example/alfs/entities/AuditLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,11 @@ public void prePersist() {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

public String getFormattedCreatedAt() {
if (createdAt == null) return "";
return createdAt.format(
java.time.format.DateTimeFormatter.ofPattern("dd MMM yyyy HH:mm")
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ public Attachment uploadToTicket(Long ticketId, MultipartFile file, User user, S
AuditAction.ATTACHMENT_ADDED,
"attachments",
null,
"objectKey:" + objectKey,
ticket
att.getFileName(),
ticket,
user
);

return att;
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/org/example/alfs/services/AuditService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.example.alfs.entities.AuditLog;
import org.example.alfs.entities.Ticket;
import org.example.alfs.entities.User;
import org.example.alfs.enums.AuditAction;
import org.example.alfs.repositories.AuditLogRepository;
import org.springframework.stereotype.Service;
Expand All @@ -15,6 +16,20 @@ public AuditService(AuditLogRepository auditLogRepository) {
this.auditLogRepository = auditLogRepository;
}


// new with user
public void log(AuditAction action, String fieldName, String oldValue, String newValue, Ticket ticket, User user) {
AuditLog log = new AuditLog();
log.setAction(action);
log.setFieldName(fieldName);
log.setOldValue(oldValue);
log.setNewValue(newValue);
log.setTicket(ticket);
log.setUser(user); // 🔥 VIKTIGT
auditLogRepository.save(log);
}

// keeping old for safety
public void log(AuditAction action, String fieldName, String oldValue, String newValue, Ticket ticket) {
AuditLog log = new AuditLog();
log.setAction(action);
Expand Down
59 changes: 58 additions & 1 deletion src/main/java/org/example/alfs/services/TicketService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.example.alfs.dto.ticket.TicketViewDTO;
import org.example.alfs.entities.Ticket;
import org.example.alfs.entities.User;
import org.example.alfs.enums.AuditAction;
import org.example.alfs.enums.Role;
import org.example.alfs.enums.TicketStatus;
import org.example.alfs.mapper.TicketMapper;
Expand All @@ -26,18 +27,22 @@ public class TicketService {
private final TicketMapper ticketMapper;
private final SecurityUtils securityUtils;
private final UserRepository userRepository;
private final AuditService auditService;

public TicketService(TicketRepository ticketRepository,
TicketMapper ticketMapper,
SecurityUtils securityUtils,
UserRepository userRepository) {
UserRepository userRepository,
AuditService auditService) {
this.ticketRepository = ticketRepository;
this.ticketMapper = ticketMapper;
this.securityUtils = securityUtils;
this.userRepository = userRepository;
this.auditService = auditService;
}

//createNewTicket
@Transactional
public TicketViewDTO createNewTicket(TicketCreateDTO dto) {

Ticket ticket = new Ticket();
Expand All @@ -58,6 +63,15 @@ public TicketViewDTO createNewTicket(TicketCreateDTO dto) {

Ticket saved = ticketRepository.save(ticket);

auditService.log(
AuditAction.CREATED,
"title",
null,
saved.getTitle(),
saved,
user
);

TicketViewDTO view = ticketMapper.entityToViewDTO(saved);

if (token != null) {
Expand Down Expand Up @@ -254,6 +268,15 @@ public TicketViewDTO updateTicketStatus(Long id, TicketStatus newStatus) {
ticket.setStatus(newStatus);

Ticket savedTicket = ticketRepository.save(ticket);

auditService.log(
AuditAction.STATUS_CHANGED,
"status",
currentStatus.name(),
newStatus.name(),
savedTicket,
user
);
return ticketMapper.entityToViewDTO(savedTicket);
}

Expand Down Expand Up @@ -299,10 +322,32 @@ public TicketViewDTO assignInvestigator(Long id, Long investigatorId) {
HttpStatus.BAD_REQUEST, "User is not an investigator");
}

TicketStatus oldStatus = ticket.getStatus();
ticket.setInvestigator(investigator);
ticket.setStatus(TicketStatus.IN_PROGRESS);

Ticket savedTicket = ticketRepository.save(ticket);


auditService.log(
AuditAction.STATUS_CHANGED,
"status",
oldStatus.name(),
TicketStatus.IN_PROGRESS.name(),
savedTicket,
user
);

auditService.log(
AuditAction.ASSIGNED,
"investigator",
null,
investigator.getUsername(),
savedTicket,
user
);


return ticketMapper.entityToViewDTO(savedTicket);
}

Expand All @@ -326,10 +371,22 @@ public TicketViewDTO unassignInvestigator(Long id) {
HttpStatus.BAD_REQUEST, "Must be IN_PROGRESS");
}

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
);

return ticketMapper.entityToViewDTO(savedTicket);
}
}
44 changes: 42 additions & 2 deletions src/main/jte/view.jte
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,49 @@

@if(auditLogs != null)
@for(var log : auditLogs)
<div class="text-sm mb-2">
${log.getAction()}

<div class="mb-3 p-2 border-b border-gray-100">

<!-- who -->
<div class="text-xs font-semibold text-gray-800">
${log.getUser() != null ? log.getUser().getUsername() : "System"}
</div>

<!-- what -->
<div class="text-sm text-gray-700">
@if(log.getAction() == org.example.alfs.enums.AuditAction.ATTACHMENT_ADDED)
File uploaded
@elseif(log.getAction() == org.example.alfs.enums.AuditAction.STATUS_CHANGED)
Status updated
@elseif(log.getAction() == org.example.alfs.enums.AuditAction.ASSIGNED)
Investigator assigned
@elseif(log.getAction() == org.example.alfs.enums.AuditAction.UNASSIGNED)
Investigator unassigned
@elseif(log.getAction() == org.example.alfs.enums.AuditAction.CREATED)
Ticket created
@else
${log.getAction().name().replace("_", " ")}
@endif
</div>

@if(log.getFieldName() != null)
<div class="text-xs text-gray-500">
<span class="font-medium">
${log.getFieldName()}:
</span>
@if(log.getOldValue() != null)
${log.getOldValue()} →
@endif
${log.getNewValue()}
</div>
@endif

<!-- when -->
<div class="text-[10px] text-gray-400">
${log.getFormattedCreatedAt()}
</div>
</div>

@endfor
@endif
</div>
Expand Down
14 changes: 14 additions & 0 deletions src/test/java/org/example/alfs/services/TicketServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.example.alfs.dto.ticket.TicketViewDTO;
import org.example.alfs.entities.Ticket;
import org.example.alfs.entities.User;
import org.example.alfs.enums.AuditAction;
import org.example.alfs.enums.Role;
import org.example.alfs.enums.TicketStatus;
import org.example.alfs.mapper.TicketMapper;
Expand Down Expand Up @@ -41,6 +42,9 @@ class TicketServiceTest {
@Mock
SecurityUtils securityUtils;

@Mock
AuditService auditService;

@InjectMocks
TicketService ticketService;

Expand Down Expand Up @@ -776,6 +780,7 @@ void unassignInvestigator_shouldSucceed() {
Ticket ticket = openTicket();
User admin = adminUser();
User investigator = investigatorUser();
investigator.setUsername("inv-user");
ticket.setInvestigator(investigator);
ticket.setStatus(TicketStatus.IN_PROGRESS);

Expand All @@ -791,6 +796,15 @@ void unassignInvestigator_shouldSucceed() {
assertNull(ticket.getInvestigator());
assertEquals(TicketStatus.OPEN, ticket.getStatus());
verify(ticketRepository).save(ticket);

verify(auditService).log(
eq(AuditAction.UNASSIGNED),
eq("investigator"),
eq("inv-user"),
isNull(),
any(),
eq(admin)
);
}

@Test
Expand Down
Loading