Create AuditEventHelper Utility Class for Easy Audit Event Recording
Objective
Create a reusable utility class to simplify audit event recording across the application, making it easy for developers to add audit trails consistently.
Current Issue
Currently, creating audit events requires repetitive code in each controller. This leads to:
- Inconsistent audit event recording
- Code duplication
- Errors in audit implementation
- Difficulty for new developers to add auditing
Task Requirements
Create Utility Class: AuditEventHelper.java
Required Methods:
public class AuditEventHelper {
/**
* Record entity creation event
*/
public static void recordCreate(String entityType, Long objectId,
Object entity, String triggerMethod) {
AuditEvent auditEvent = new AuditEvent();
auditEvent.setEntityType(entityType);
auditEvent.setObjectId(objectId);
auditEvent.setEventTrigger(triggerMethod);
auditEvent.setAfterJson(convertToJson(entity));
auditEvent.setEventDataTime(new Date());
auditEvent.setWebUserId(getCurrentUser().getId());
auditEvent.setIpAddress(getClientIpAddress());
saveAuditEvent(auditEvent);
}
/**
* Record entity update event
*/
public static void recordUpdate(String entityType, Long objectId,
Object beforeEntity, Object afterEntity,
String triggerMethod) {
AuditEvent auditEvent = new AuditEvent();
auditEvent.setEntityType(entityType);
auditEvent.setObjectId(objectId);
auditEvent.setEventTrigger(triggerMethod);
auditEvent.setBeforeJson(convertToJson(beforeEntity));
auditEvent.setAfterJson(convertToJson(afterEntity));
auditEvent.setEventDataTime(new Date());
auditEvent.setWebUserId(getCurrentUser().getId());
auditEvent.setIpAddress(getClientIpAddress());
saveAuditEvent(auditEvent);
}
/**
* Record entity deletion event
*/
public static void recordDelete(String entityType, Long objectId,
Object entity, String reason,
String triggerMethod) {
AuditEvent auditEvent = new AuditEvent();
auditEvent.setEntityType(entityType);
auditEvent.setObjectId(objectId);
auditEvent.setEventTrigger(triggerMethod);
auditEvent.setBeforeJson(convertToJson(entity));
Map<String, String> deletionInfo = new HashMap<>();
deletionInfo.put("deleted", "true");
deletionInfo.put("reason", reason);
deletionInfo.put("deletedAt", new Date().toString());
auditEvent.setAfterJson(convertToJson(deletionInfo));
auditEvent.setEventDataTime(new Date());
auditEvent.setWebUserId(getCurrentUser().getId());
auditEvent.setIpAddress(getClientIpAddress());
saveAuditEvent(auditEvent);
}
/**
* Record custom audit event with specific before/after values
*/
public static void recordCustom(String entityType, Long objectId,
Map<String, Object> beforeData,
Map<String, Object> afterData,
String triggerMethod) {
AuditEvent auditEvent = new AuditEvent();
auditEvent.setEntityType(entityType);
auditEvent.setObjectId(objectId);
auditEvent.setEventTrigger(triggerMethod);
auditEvent.setBeforeJson(convertToJson(beforeData));
auditEvent.setAfterJson(convertToJson(afterData));
auditEvent.setEventDataTime(new Date());
auditEvent.setWebUserId(getCurrentUser().getId());
auditEvent.setIpAddress(getClientIpAddress());
saveAuditEvent(auditEvent);
}
// Helper methods
private static String convertToJson(Object obj) {
// Use Gson or Jackson to convert to JSON
}
private static WebUser getCurrentUser() {
// Get from session
}
private static String getClientIpAddress() {
// Get from HTTP request
}
private static void saveAuditEvent(AuditEvent event) {
// Save to audit database
}
}
Usage Examples:
For Patient Creation:
// In PatientController.savePatient()
Patient savedPatient = patientFacade.save(patient);
AuditEventHelper.recordCreate("Patient", savedPatient.getId(),
savedPatient, "savePatient");
For Patient Update:
// In PatientController.updatePatient()
Patient originalPatient = clonePatient(patient); // Clone before changes
// ... apply changes ...
Patient updatedPatient = patientFacade.save(patient);
AuditEventHelper.recordUpdate("Patient", patient.getId(),
originalPatient, updatedPatient,
"updatePatient");
For Bill Delete:
// In BillController.cancelBill()
Bill billToCancel = billFacade.find(billId);
AuditEventHelper.recordDelete("Bill", billId, billToCancel,
"Duplicate bill", "cancelBill");
Implementation Steps:
- Create
AuditEventHelper.javain utilities package - Implement JSON conversion using Gson or Jackson library
- Implement user session retrieval method
- Implement IP address extraction from HTTP request
- Implement audit database save logic
- Add error handling (audit failures shouldn't break main operations)
- Add logging for audit operations
- Create unit tests for all methods
- Document usage with examples
Acceptance Criteria:
- [ ] AuditEventHelper class created with all methods
- [ ] JSON conversion works correctly for entities
- [ ] Current user is captured automatically
- [ ] IP address is captured automatically
- [ ] Audit events are saved to audit database
- [ ] Error handling prevents audit failures from breaking operations
- [ ] Unit tests cover all methods
- [ ] Documentation with usage examples
- [ ] At least one controller uses the helper class successfully
Testing Checklist:
- Create the utility class
- Test recordCreate() with a sample Patient entity
- Verify JSON is properly formed in database
- Test recordUpdate() with before/after entities
- Verify both JSONs are captured
- Test recordDelete() with reason
- Test that audit failures don't break main operations
- Verify user and IP are captured correctly
Priority: High Estimated Effort: 4-5 hours Skills: Java, JSON (Gson/Jackson), Utilities, Session Management
Benefits
- Consistency: All audit events follow same pattern
- Ease of Use: One-line audit recording
- Maintainability: Centralized audit logic
- Error Handling: Consistent error handling
- Developer Friendly: Easy for new developers to add auditing
- Reduces Bugs: Less chance of missing fields or incorrect setup
This utility will make it much easier to implement all the other audit event issues.
@jbigler thanks for the feedback. That's an interesting idea. I wonder if we could provide some form of callback option for users to specify any custom command like desktop notification. This would remove the support for different os within retest.