Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.openmrs.module.auditlogweb.api.dto.AuditLogResponseDto;
import org.openmrs.module.auditlogweb.api.utils.AuditLogConstants;
import org.openmrs.module.webservices.rest.web.RestConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -28,6 +30,8 @@
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* REST controller for exposing audit log entries via the OpenMRS REST API.
Expand All @@ -48,11 +52,19 @@
@RequestMapping("/rest/" + RestConstants.VERSION_1 + "/auditlogs")
public class AuditLogRestController {

private static final Logger log = LoggerFactory.getLogger(AuditLogRestController.class);

private static final Map<String, String> VALID_EVENT_TYPES = Map.of(
"ADDED", "Record was added",
"MODIFIED", "Record was modified",
"DELETED", "Record was deleted"
);

private final AuditService auditService;

/**
* Retrieves paginated audit log entries with optional filters:
* user ID, username, date range, and entity type.
* user ID, username, date range, entity type, and event type.
*
* @param page zero-based page index
* @param size number of results per page
Expand All @@ -61,6 +73,7 @@ public class AuditLogRestController {
* @param startDate optional start date ("dd/MM/yyyy")
* @param endDate optional end date ("dd/MM/yyyy")
* @param entityType optional entity type filter
* @param eventType optional event type filter (ADDED, MODIFIED, or DELETED)
* @return a structured response containing audit log entries
* @throws ResponseStatusException if input is invalid
*/
Expand All @@ -72,21 +85,34 @@ public AuditLogResponseDto getAuditLogs(
@RequestParam(required = false) String username,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@RequestParam(required = false) String entityType
@RequestParam(required = false) String entityType,
@RequestParam(required = false) String eventType
) {
if (page < 0) page = 0;
if (size <= 0) size = 20;

String resolvedEventType = validateAndResolveEventType(eventType);

Integer effectiveUserId = resolveUserId(userId, username);
Date start = parseDate(startDate);
Date end = parseDate(endDate);

boolean fullDetails = userId != null || username != null || startDate != null || endDate != null || entityType != null;
boolean fullDetails = userId != null || username != null || startDate != null
|| endDate != null || entityType != null || eventType != null;

List<AuditLogDetailDTO> auditDetails = auditService.mapAuditEntitiesToDetails(
auditService.getAllRevisionsAcrossEntitiesWithEntityType(page, size, effectiveUserId, start, end, entityType, "desc")
);

// TODO: Push eventType filtering down to AuditService/DAO for better performance.
// Currently filtered in-memory because the service layer does not yet accept eventType.
if (resolvedEventType != null) {
log.debug("Filtering audit logs by eventType: {}", eventType);
auditDetails = auditDetails.stream()
.filter(d -> resolvedEventType.equals(d.getEventType()))
.collect(Collectors.toList());
}

if (!fullDetails) {
auditDetails.forEach(d -> d.setChanges(Collections.emptyList()));
}
Expand Down Expand Up @@ -115,6 +141,26 @@ private Date parseDate(String dateStr) {
}
}

/**
* Validates the eventType parameter and resolves it to the internal DTO representation.
*
* @param eventType the raw query parameter value (e.g., "ADDED", "modified")
* @return the resolved internal event type string, or null if the input is null/empty
* @throws ResponseStatusException if the value is not a recognised event type
*/
private String validateAndResolveEventType(String eventType) {
if (eventType == null || eventType.trim().isEmpty()) {
return null;
}
String normalized = eventType.trim().toUpperCase();
String resolved = VALID_EVENT_TYPES.get(normalized);
if (resolved == null) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Invalid eventType: '" + eventType + "'. Allowed values: " + VALID_EVENT_TYPES.keySet());
}
return resolved;
}

private Integer resolveUserId(Integer userId, String username) {
if (userId != null) return userId;
if (username != null && !username.isEmpty()) {
Expand Down