Skip to content

Commit

Permalink
fix: (2.39) Add audit change logs when updating event data value indi…
Browse files Browse the repository at this point in the history
…vidually [DHIS2-18138] (#18878)
  • Loading branch information
ameenhere authored Oct 21, 2024
1 parent 9b4a329 commit 3a9e11b
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ public ImportSummary updateEventDataValues(
eventPersistenceService.updateEventDataValues(de, event, context);
}

auditTrackedEntityDataValueHistory(event, context, new Date());

eventPersistenceService.updateTrackedEntityInstances(context, List.of(event));

executorsByPhase.get(EventProcessorPhase.UPDATE_POST).execute(context, List.of(event));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.hisp.dhis.dxf2.events.importer.EventProcessorExecutor;
import org.hisp.dhis.dxf2.events.importer.EventProcessorPhase;
import org.hisp.dhis.dxf2.events.importer.context.WorkContext;
import org.hisp.dhis.program.ProgramStageInstance;
import org.hisp.dhis.trackedentitydatavalue.TrackedEntityDataValueAuditService;
import org.hisp.dhis.user.CurrentUserService;
import org.junit.jupiter.api.Test;
Expand All @@ -69,6 +70,8 @@ class EventManagerTest {

@Mock TrackedEntityDataValueAuditService entityDataValueAuditService;

@Mock WorkContext workContext;

@Mock List<Checker> checkersRunOnDelete;

@Mock CurrentUserService currentUserService;
Expand All @@ -82,15 +85,16 @@ void shouldTriggerUpdatePostProcessorsWhenEventDataValuesUpdated()
throws JsonProcessingException {

Event event = new Event();
WorkContext workContext = WorkContext.builder().build();
event.setUid("id");
doNothing()
.when(eventPersistenceService)
.updateTrackedEntityInstances(any(WorkContext.class), anyList());

doNothing().when(eventProcessorExecutor).execute(any(WorkContext.class), anyList());

when(executorsByPhase.get(any(EventProcessorPhase.class))).thenReturn(eventProcessorExecutor);

when(workContext.getPersistedProgramStageInstanceMap())
.thenReturn(Map.of("id", new ProgramStageInstance()));
subject.updateEventDataValues(event, Set.of(), workContext);

verify(eventPersistenceService, times(1))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hisp.dhis.tracker.Assertions.assertTrackedEntityDataValueAudit;
import static org.hisp.dhis.user.UserRole.AUTHORITY_ALL;
import static org.hisp.dhis.util.DateUtils.getIso8601NoTz;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand All @@ -58,6 +60,7 @@
import org.hisp.dhis.category.Category;
import org.hisp.dhis.category.CategoryCombo;
import org.hisp.dhis.category.CategoryOption;
import org.hisp.dhis.common.AuditType;
import org.hisp.dhis.common.CodeGenerator;
import org.hisp.dhis.common.DataDimensionType;
import org.hisp.dhis.common.IdentifiableObjectManager;
Expand Down Expand Up @@ -92,8 +95,11 @@
import org.hisp.dhis.program.ProgramType;
import org.hisp.dhis.program.UserInfoSnapshot;
import org.hisp.dhis.test.integration.TransactionalIntegrationTest;
import org.hisp.dhis.trackedentity.TrackedEntityDataValueAuditQueryParams;
import org.hisp.dhis.trackedentity.TrackedEntityType;
import org.hisp.dhis.trackedentity.TrackedEntityTypeService;
import org.hisp.dhis.trackedentitydatavalue.TrackedEntityDataValueAudit;
import org.hisp.dhis.trackedentitydatavalue.TrackedEntityDataValueAuditService;
import org.hisp.dhis.user.User;
import org.hisp.dhis.user.UserService;
import org.hisp.dhis.util.DateUtils;
Expand All @@ -116,6 +122,8 @@ class EventImportTest extends TransactionalIntegrationTest {

@Autowired private TrackedEntityInstanceService trackedEntityInstanceService;

@Autowired private TrackedEntityDataValueAuditService entityDataValueAuditService;

@Autowired private ProgramStageDataElementService programStageDataElementService;

@Autowired private EnrollmentService enrollmentService;
Expand Down Expand Up @@ -271,9 +279,10 @@ void shouldUpdateEventDataValuesWhenAddingDataValuesToEvent() throws IOException
"10");
String uid = eventService.addEventsJson(is, null).getImportSummaries().get(0).getReference();

Event event = createEvent(uid);
Event newEvent = createEvent(uid);

ProgramStageInstance ev = programStageInstanceService.getProgramStageInstance(event.getUid());
ProgramStageInstance ev =
programStageInstanceService.getProgramStageInstance(newEvent.getUid());

assertNotNull(ev);
assertEquals(1, ev.getEventDataValues().size());
Expand All @@ -290,15 +299,15 @@ void shouldUpdateEventDataValuesWhenAddingDataValuesToEvent() throws IOException
dataValueB.setDataElement(dataElementB.getUid());
dataValueB.setStoredBy(superUser.getName());

event.setDataValues(Set.of(dataValueA, dataValueB));
newEvent.setDataValues(Set.of(dataValueA, dataValueB));

Date now = new Date();

eventService.updateEventDataValues(event);
eventService.updateEventDataValues(newEvent);

manager.clear();

ev = programStageInstanceService.getProgramStageInstance(event.getUid());
ev = programStageInstanceService.getProgramStageInstance(newEvent.getUid());

assertNotNull(ev);
assertNotNull(ev.getEventDataValues());
Expand Down Expand Up @@ -338,6 +347,116 @@ void shouldUpdateEventDataValuesWhenAddingDataValuesToEvent() throws IOException
assertTrue(trackedEntityInstance.getLastUpdated().compareTo(getIso8601NoTz(now)) > 0);
}

@Test
void shouldAuditChangelogWhenUpdatingEventDataValues() throws IOException {
String previousValueB = "10";
String newValueB = "15";
String newValueA = "20";
InputStream is =
createEventJsonInputStream(
programB.getUid(),
programStageB.getUid(),
organisationUnitB.getUid(),
trackedEntityInstanceMaleA.getTrackedEntityInstance(),
dataElementB,
previousValueB);
String uid = eventService.addEventsJson(is, null).getImportSummaries().get(0).getReference();

Event newEvent = createEvent(uid);

ProgramStageInstance ev =
programStageInstanceService.getProgramStageInstance(newEvent.getUid());

assertNotNull(ev);
assertEquals(1, ev.getEventDataValues().size());

// add a new data value and update an existing one

DataValue dataValueA = new DataValue();
dataValueA.setValue(newValueA);
dataValueA.setDataElement(dataElementA.getUid());
dataValueA.setStoredBy(superUser.getName());

DataValue dataValueB = new DataValue();
dataValueB.setValue(newValueB);
dataValueB.setDataElement(dataElementB.getUid());
dataValueB.setStoredBy(superUser.getName());

newEvent.setDataValues(Set.of(dataValueA, dataValueB));

eventService.updateEventDataValues(newEvent);

List<TrackedEntityDataValueAudit> createdAudits =
entityDataValueAuditService.getTrackedEntityDataValueAudits(
new TrackedEntityDataValueAuditQueryParams()
.setDataElements(List.of(dataElementA))
.setProgramStageInstances(List.of(ev))
.setAuditTypes(List.of(AuditType.CREATE)));

List<TrackedEntityDataValueAudit> updatedAudits =
entityDataValueAuditService.getTrackedEntityDataValueAudits(
new TrackedEntityDataValueAuditQueryParams()
.setDataElements(List.of(dataElementB))
.setProgramStageInstances(List.of(ev))
.setAuditTypes(List.of(AuditType.UPDATE)));

assertFalse(createdAudits.isEmpty());
assertFalse(updatedAudits.isEmpty());
assertEquals(1, createdAudits.size());
assertEquals(1, updatedAudits.size());

assertTrackedEntityDataValueAudit(
createdAudits.get(0), dataElementA, AuditType.CREATE, newValueA);
assertTrackedEntityDataValueAudit(
updatedAudits.get(0), dataElementB, AuditType.UPDATE, previousValueB);
}

@Test
void shouldAuditChangelogWhenDeletingEventDataValue() throws IOException {
String previousValueB = "10";
InputStream is =
createEventJsonInputStream(
programB.getUid(),
programStageB.getUid(),
organisationUnitB.getUid(),
trackedEntityInstanceMaleA.getTrackedEntityInstance(),
dataElementB,
"10");
String uid = eventService.addEventsJson(is, null).getImportSummaries().get(0).getReference();

Event createdEvent = createEvent(uid);

ProgramStageInstance ev =
programStageInstanceService.getProgramStageInstance(createdEvent.getUid());

assertNotNull(ev);
assertEquals(1, ev.getEventDataValues().size());

// delete data Element in Event Data Values by setting its value to null

DataValue dataValueB = new DataValue();
dataValueB.setValue(null);
dataValueB.setDataElement(dataElementB.getUid());
dataValueB.setStoredBy(superUser.getName());

createdEvent.setDataValues(Set.of(dataValueB));

eventService.updateEventDataValues(createdEvent);

List<TrackedEntityDataValueAudit> deleteAudits =
entityDataValueAuditService.getTrackedEntityDataValueAudits(
new TrackedEntityDataValueAuditQueryParams()
.setDataElements(List.of(dataElementB))
.setProgramStageInstances(List.of(ev))
.setAuditTypes(List.of(AuditType.DELETE)));

assertFalse(deleteAudits.isEmpty());
assertEquals(1, deleteAudits.size());

assertTrackedEntityDataValueAudit(
deleteAudits.get(0), dataElementB, AuditType.DELETE, previousValueB);
}

@Test
void shouldDeleteDataElementFromEventDataValuesWhenSetDataValueToNull() throws IOException {
InputStream is =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
import java.util.Arrays;
import java.util.Date;
import java.util.function.Supplier;
import org.hisp.dhis.common.AuditType;
import org.hisp.dhis.dataelement.DataElement;
import org.hisp.dhis.trackedentitydatavalue.TrackedEntityDataValueAudit;
import org.hisp.dhis.tracker.report.TrackerErrorCode;
import org.hisp.dhis.tracker.report.TrackerImportReport;
import org.hisp.dhis.tracker.report.TrackerStatus;
Expand Down Expand Up @@ -198,6 +201,47 @@ public static void assertHasTimeStamp(String date) {
String.format("Supported format is %s but found %s", DATE_WITH_TIMESTAMP_PATTERN, date));
}

/**
* assertTrackedEntityDataValueAudit asserts a TrackedEntityDataValueAudit obtained from the db
* and compares it with the expected value, auditType and dataElement.
*
* @param audit The TrackedEntityDataValueAudit entity obtained from persistence
* @param expectedDataElement The audit object is expected to be for this dataElement
* @param expectedAuditType The audit object is expected to have this auditType
* @param expectedValue The audit object is expected to have this value
*/
public static void assertTrackedEntityDataValueAudit(
TrackedEntityDataValueAudit audit,
DataElement expectedDataElement,
AuditType expectedAuditType,
String expectedValue) {
assertAll(
() -> assertNotNull(audit),
() ->
assertEquals(
expectedAuditType,
audit.getAuditType(),
() ->
"Expected audit type is "
+ expectedAuditType
+ " but found "
+ audit.getAuditType()),
() ->
assertEquals(
audit.getDataElement().getUid(),
expectedDataElement.getUid(),
() ->
"Expected dataElement is "
+ expectedDataElement.getUid()
+ " but found "
+ audit.getDataElement().getUid()),
() ->
assertEquals(
expectedValue,
audit.getValue(),
() -> "Expected value is " + expectedValue + " but found " + audit.getValue()));
}

private static boolean hasTimeStamp(Date date) {
try {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
package org.hisp.dhis.tracker.bundle;

import static org.hisp.dhis.tracker.Assertions.assertNoErrors;
import static org.hisp.dhis.tracker.Assertions.assertTrackedEntityDataValueAudit;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;

Expand Down Expand Up @@ -114,42 +114,15 @@ void testTrackedEntityDataValueAuditCreate() throws IOException {
assertAll(
() -> assertNotNull(createdAudit),
() -> assertNotNull(updatedAudit),
() -> assertNotNull(deletedAudit));
assertAuditCollection(createdAudit, AuditType.CREATE, ORIGINAL_VALUE);
assertAuditCollection(updatedAudit, AuditType.UPDATE, ORIGINAL_VALUE);
assertAuditCollection(deletedAudit, AuditType.DELETE, UPDATED_VALUE);
}

private void assertAuditCollection(
List<TrackedEntityDataValueAudit> audits, AuditType auditType, String expectedValue) {
assertAll(
() -> assertFalse(audits.isEmpty()),
() ->
assertEquals(
auditType,
audits.get(0).getAuditType(),
() ->
"Expected audit type is "
+ auditType
+ " but found "
+ audits.get(0).getAuditType()),
() ->
assertEquals(
audits.get(0).getDataElement().getUid(),
dataElement.getUid(),
() ->
"Expected dataElement is "
+ dataElement.getUid()
+ " but found "
+ audits.get(0).getDataElement().getUid()),
() ->
assertEquals(
expectedValue,
audits.get(0).getValue(),
() ->
"Expected value is "
+ expectedValue
+ " but found "
+ audits.get(0).getValue()));
() -> assertNotNull(deletedAudit),
() -> assertFalse(createdAudit.isEmpty()),
() -> assertFalse(updatedAudit.isEmpty()),
() -> assertFalse(deletedAudit.isEmpty()));
assertTrackedEntityDataValueAudit(
createdAudit.get(0), dataElement, AuditType.CREATE, ORIGINAL_VALUE);
assertTrackedEntityDataValueAudit(
updatedAudit.get(0), dataElement, AuditType.UPDATE, ORIGINAL_VALUE);
assertTrackedEntityDataValueAudit(
deletedAudit.get(0), dataElement, AuditType.DELETE, UPDATED_VALUE);
}
}

0 comments on commit 3a9e11b

Please sign in to comment.