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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import uk.gov.hmcts.darts.common.entity.DefenceEntity;
import uk.gov.hmcts.darts.common.entity.DefendantEntity;
import uk.gov.hmcts.darts.common.entity.EventEntity;
import uk.gov.hmcts.darts.common.entity.EventLinkedCaseEntity;
import uk.gov.hmcts.darts.common.entity.HearingEntity;
import uk.gov.hmcts.darts.common.entity.ProsecutorEntity;
import uk.gov.hmcts.darts.common.entity.TranscriptionCommentEntity;
Expand All @@ -37,7 +38,11 @@
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -66,6 +71,38 @@ void positiveRetentionDatePassed() {
assertCase(caseId1, true);
}

@Test
@DisplayName("Two cases linked to the same event, one case has passed retention date, the other has not. Event should not be anonymised")
void retentionDatePassedForOneCaseButNotAnotherEventNotAnoymised() {
CourtCaseEntity courtCase1 = createCase(-1, CaseRetentionStatus.COMPLETE);
CourtCaseEntity courtCase2 = createCase(-1, CaseRetentionStatus.PENDING);

EventEntity event = dartsDatabase.getEventLinkedCaseRepository().findAllByCourtCase(courtCase1).getFirst().getEvent();
eventLinkedCaseStub.createCaseLinkedEvent(event, courtCase2);
final int caseId1 = courtCase1.getId();

caseExpiryDeletionAutomatedTask.preRunTask();
caseExpiryDeletionAutomatedTask.runTask();

assertCase(caseId1, true, event.getId());
}

@Test
@DisplayName("Two cases linked to the same event, both cases have passed retention date. Event should be anonymised")
void retentionDatePassedForBothCaseLinkedEventsAnoymised() {
CourtCaseEntity courtCase1 = createCase(-1, CaseRetentionStatus.COMPLETE);
CourtCaseEntity courtCase2 = createCase(-1, CaseRetentionStatus.COMPLETE);

EventEntity event = dartsDatabase.getEventLinkedCaseRepository().findAllByCourtCase(courtCase1).getFirst().getEvent();
eventLinkedCaseStub.createCaseLinkedEvent(event, courtCase2);
final int caseId1 = courtCase1.getId();

caseExpiryDeletionAutomatedTask.preRunTask();
caseExpiryDeletionAutomatedTask.runTask();

assertCase(caseId1, true);
}


@Test
void positiveRetentionDateNotPassed() {
Expand Down Expand Up @@ -111,7 +148,7 @@ void positiveMultipleToAnonymiseAndSomeNotTo() {
}


private void assertCase(int caseId, boolean isAnonymised) {
private void assertCase(int caseId, boolean isAnonymised, int... excludeEventIds) {
transactionalUtil.executeInTransaction(() -> {
CourtCaseEntity courtCase = dartsDatabase.getCourtCaseStub().getCourtCase(caseId);
assertThat(courtCase.isDataAnonymised())
Expand Down Expand Up @@ -140,7 +177,15 @@ private void assertCase(int caseId, boolean isAnonymised) {

courtCase.getHearings().forEach(hearingEntity -> assertHearing(hearingEntity, isAnonymised));

dartsDatabase.getEventRepository().findAllByCaseId(caseId).forEach(eventEntity -> assertEvent(eventEntity, isAnonymised));
Set<Integer> eventIdsToExclude = new HashSet<>();
Arrays.stream(excludeEventIds).forEach(eventIdsToExclude::add);
List<EventLinkedCaseEntity> eventLinkedCaseEntities = new ArrayList<>();
eventLinkedCaseEntities.addAll(dartsDatabase.getEventLinkedCaseRepository().findAllByCourtCase(courtCase));
eventLinkedCaseEntities.addAll(dartsDatabase.getEventLinkedCaseRepository().findAllByCaseNumberAndCourthouseName(
courtCase.getCaseNumber(),
courtCase.getCourthouse().getCourthouseName()));
eventLinkedCaseEntities.stream().map(EventLinkedCaseEntity::getEvent)
.forEach(eventEntity -> assertEvent(eventEntity, !eventIdsToExclude.contains(eventEntity.getId()) && isAnonymised));

assertAuditEntries(courtCase, isAnonymised);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,15 @@ public class PostgresIntegrationBase {
*/
private static final int SERVER_MAX_CONNECTIONS = 50;

private static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(
"postgres:16-alpine"
).withDatabaseName("darts")
.withUsername("darts")
.withPassword("darts");
private static final PostgreSQLContainer<?> POSTGRES;

static {
POSTGRES = new PostgreSQLContainer<>(
"postgres:16-alpine"
).withDatabaseName("darts")
.withUsername("darts")
.withPassword("darts");
}

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
Expand All @@ -73,4 +77,4 @@ void clearDb() {
void clearTestData() {
logAppender.reset();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package uk.gov.hmcts.darts.common.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import uk.gov.hmcts.darts.common.entity.CourtCaseEntity;
import uk.gov.hmcts.darts.common.entity.EventEntity;
import uk.gov.hmcts.darts.common.entity.EventLinkedCaseEntity;

import java.util.List;
Expand All @@ -13,4 +15,16 @@ public interface EventLinkedCaseRepository extends JpaRepository<EventLinkedCase
List<EventLinkedCaseEntity> findAllByCourtCase(CourtCaseEntity courtCase);

List<EventLinkedCaseEntity> findAllByCaseNumberAndCourthouseName(String caseNumber, String courthouseName);

@Query("""
SELECT COUNT(DISTINCT cc) = (COUNT(cc.isDataAnonymised) filter (where cc.isDataAnonymised = true))
FROM EventLinkedCaseEntity elc
LEFT JOIN CourtCaseEntity cc
ON (elc.courtCase = cc
or (cc.courthouse.courthouseName = elc.courthouseName and cc.caseNumber = elc.caseNumber))
WHERE elc.event = :eventEntity
group by elc.event
"""
)
boolean areAllAssociatedCasesAnonymised(EventEntity eventEntity);
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,13 @@ void anonymiseCourtCaseEntity(UserAccountEntity userAccount, CourtCaseEntity cou
courtCase.getDefenceList().forEach(defenceEntity -> anonymiseDefenceEntity(userAccount, defenceEntity));
courtCase.getProsecutorList().forEach(prosecutorEntity -> anonymiseProsecutorEntity(userAccount, prosecutorEntity));
courtCase.getHearings().forEach(hearingEntity -> anonymiseHearingEntity(userAccount, hearingEntity));
anonymiseAllEventsFromCase(userAccount, courtCase);

courtCase.markAsExpired(userAccount);
//This also saves defendant, defence and prosecutor entities
tidyUpTransformedMediaEntities(userAccount, courtCase);
auditApi.record(AuditActivity.CASE_EXPIRED, userAccount, courtCase);
anonymiseCreatedModifiedBaseEntity(userAccount, courtCase);
caseService.saveCase(courtCase);
anonymiseAllEventsFromCase(userAccount, courtCase);

//Required for Dynatrace dashboards
logApi.caseDeletedDueToExpiry(courtCase.getId(), courtCase.getCaseNumber());
Expand Down Expand Up @@ -108,16 +107,24 @@ public void anonymiseEventByIds(UserAccountEntity userAccount, List<Integer> eve

@Override
public void anonymiseEvent(UserAccountEntity userAccount, EventEntity eventEntity) {
anonymiseEventEntity(userAccount, eventEntity);
eventService.saveEvent(eventEntity);
anonymiseEventEntity(userAccount, eventEntity, false);
auditApi.record(AuditActivity.MANUAL_OBFUSCATION, userAccount, eventEntity.getId().toString());
logApi.manualObfuscation(eventEntity);
}

void anonymiseEventEntity(UserAccountEntity userAccount, EventEntity eventEntity) {
void anonymiseEventEntity(UserAccountEntity userAccount, EventEntity eventEntity, boolean onlyAnonymiseIfAllCasesExpired) {
if (eventEntity.isDataAnonymised()) {
log.debug("Event {} already anonymised skipping", eventEntity.getId());
return;
}
if (onlyAnonymiseIfAllCasesExpired && !eventService.allAssociatedCasesAnonymised(eventEntity)) {
log.debug("Event {} not anonymised as not all cases are expired", eventEntity.getId());
return;
}
eventEntity.setEventText(UUID.randomUUID().toString());
eventEntity.setDataAnonymised(true);
anonymiseCreatedModifiedBaseEntity(userAccount, eventEntity);
eventService.saveEvent(eventEntity);
}

void anonymiseTranscriptionEntity(UserAccountEntity userAccount, TranscriptionEntity transcriptionEntity) {
Expand All @@ -127,14 +134,17 @@ void anonymiseTranscriptionEntity(UserAccountEntity userAccount, TranscriptionEn
}

void anonymiseTranscriptionCommentEntity(UserAccountEntity userAccount, TranscriptionCommentEntity transcriptionCommentEntity) {
if (transcriptionCommentEntity.isDataAnonymised()) {
log.debug("Transcription comment {} already anonymised skipping", transcriptionCommentEntity.getId());
return;
}
transcriptionCommentEntity.setComment(UUID.randomUUID().toString());
transcriptionCommentEntity.setDataAnonymised(true);
anonymiseCreatedModifiedBaseEntity(userAccount, transcriptionCommentEntity);
}

void anonymiseTranscriptionWorkflowEntity(TranscriptionWorkflowEntity transcriptionWorkflowEntity) {
transcriptionWorkflowEntity.close();

}

void tidyUpTransformedMediaEntities(UserAccountEntity userAccount, CourtCaseEntity courtCase) {
Expand Down Expand Up @@ -190,6 +200,6 @@ private void anonymiseCreatedModifiedBaseEntity(UserAccountEntity userAccount, C
}

private void anonymiseAllEventsFromCase(UserAccountEntity userAccount, CourtCaseEntity courtCase) {
eventService.getAllCourtCaseEventVersions(courtCase).forEach(eventEntity -> anonymiseEventEntity(userAccount, eventEntity));
eventService.getAllCourtCaseEventVersions(courtCase).forEach(eventEntity -> anonymiseEventEntity(userAccount, eventEntity, true));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface EventService {
EventEntity saveEvent(EventEntity eventEntity);

Set<EventEntity> getAllCourtCaseEventVersions(CourtCaseEntity courtCase);

boolean allAssociatedCasesAnonymised(EventEntity eventEntity);
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ public Set<EventEntity> getAllCourtCaseEventVersions(CourtCaseEntity courtCase)
return allEvents;
}

@Override
public boolean allAssociatedCasesAnonymised(EventEntity eventEntity) {
return eventLinkedCaseRepository.areAllAssociatedCasesAnonymised(eventEntity);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.hmcts.darts.common.service.impl;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
Expand Down Expand Up @@ -87,16 +88,64 @@ private void assertLastModifiedByAndAt(CreatedModifiedBaseEntity entity, UserAcc


@Test
void positiveEventEntityAnonymise() {
@DisplayName("Event should not be anonymised if one or more assocaited cases are not anonymised")
void eventEntityAnonymiseNotUpdatedAsNotAllCasesExpiredAndOnlyAnonymiseIfAllCasesExpiredIsTrue() {
EventEntity eventEntity = new EventEntity();
eventEntity.setEventText("event text");

UserAccountEntity userAccount = new UserAccountEntity();
when(eventService.allAssociatedCasesAnonymised(eventEntity)).thenReturn(false);
dataAnonymisationService.anonymiseEventEntity(userAccount, eventEntity, true);
assertThat(eventEntity.getEventText()).isEqualTo("event text");
assertThat(eventEntity.isDataAnonymised()).isFalse();
verify(eventService).allAssociatedCasesAnonymised(eventEntity);
verify(eventService, never()).saveEvent(eventEntity);
}

@Test
void positiveEventEntityAnonymiseUpdatedAsAllCasesExpiredAndOnlyAnonymiseIfAllCasesExpiredIsTrue() {
setupOffsetDateTime();
EventEntity eventEntity = new EventEntity();
eventEntity.setEventText("event text");

UserAccountEntity userAccount = new UserAccountEntity();
dataAnonymisationService.anonymiseEventEntity(userAccount, eventEntity);
when(eventService.allAssociatedCasesAnonymised(eventEntity)).thenReturn(true);
dataAnonymisationService.anonymiseEventEntity(userAccount, eventEntity, true);
assertThat(eventEntity.getEventText()).matches(TestUtils.UUID_REGEX);
assertThat(eventEntity.isDataAnonymised()).isTrue();
assertLastModifiedByAndAt(eventEntity, userAccount);
verify(eventService).allAssociatedCasesAnonymised(eventEntity);
verify(eventService).saveEvent(eventEntity);
}

@Test
@DisplayName("Event should be anonymised if one or more assocaited cases are not anonymised and the onlyAnonymiseIfAllCasesExpired flag is false")
void eventEntityAnonymiseUpdatedAsNotAllCasesExpiredAndOnlyAnonymiseIfAllCasesExpiredIsFalse() {
setupOffsetDateTime();
EventEntity eventEntity = new EventEntity();
eventEntity.setEventText("event text");

UserAccountEntity userAccount = new UserAccountEntity();
dataAnonymisationService.anonymiseEventEntity(userAccount, eventEntity, false);
assertThat(eventEntity.getEventText()).matches(TestUtils.UUID_REGEX);
assertThat(eventEntity.isDataAnonymised()).isTrue();
assertLastModifiedByAndAt(eventEntity, userAccount);
verify(eventService).saveEvent(eventEntity);
}

@Test
@DisplayName("Event should not be anonymised again if the event is already anonymised")
void eventEntityNotUpdatedAsAlreadyAnonymised() {
EventEntity eventEntity = new EventEntity();
eventEntity.setEventText("event text");
eventEntity.setDataAnonymised(true);

UserAccountEntity userAccount = new UserAccountEntity();
dataAnonymisationService.anonymiseEventEntity(userAccount, eventEntity, false);
assertThat(eventEntity.getEventText()).isEqualTo("event text");
assertThat(eventEntity.isDataAnonymised()).isTrue();
verifyNoMoreInteractions(eventService);
verify(eventService, never()).saveEvent(eventEntity);
}

@Test
Expand Down Expand Up @@ -201,8 +250,8 @@ void assertPositiveAnonymiseCourtCaseEntity() {
verify(dataAnonymisationService, times(1)).anonymiseHearingEntity(userAccount, hearingEntity1);
verify(dataAnonymisationService, times(1)).anonymiseHearingEntity(userAccount, hearingEntity2);

verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, entityEntity1);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, entityEntity2);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, entityEntity1, true);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, entityEntity2, true);

verify(dataAnonymisationService, times(1)).tidyUpTransformedMediaEntities(userAccount, courtCase);
verify(caseService, times(1)).saveCase(courtCase);
Expand Down Expand Up @@ -409,9 +458,9 @@ void positiveAnonymiseEventByIds() {
dataAnonymisationService.anonymiseEventByIds(userAccount, List.of(1, 2, 3, 4));


verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, event1);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, event2);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, event3);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, event1, false);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, event2, false);
verify(dataAnonymisationService, times(1)).anonymiseEventEntity(userAccount, event3, false);

verify(eventService, times(1)).getEventByEveId(1);
verify(eventService, times(1)).getEventByEveId(2);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.gov.hmcts.darts.event.service.impl;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
Expand Down Expand Up @@ -98,4 +99,20 @@ void positiveGetAllCourtCaseEventVersions() {
}


@Test
@DisplayName("areAllAssociatedCasesAnonymised(...) method, should return true if all associated cases are anonymised")
void allAssociatedCasesAnonymisedTrue() {
EventEntity event = mock(EventEntity.class);
when(eventLinkedCaseRepository.areAllAssociatedCasesAnonymised(event)).thenReturn(true);
assertThat(eventService.allAssociatedCasesAnonymised(event)).isTrue();
verify(eventLinkedCaseRepository).areAllAssociatedCasesAnonymised(event);
}

@Test
void positiveAllAssociatedCasesAnonymisedFalse() {
EventEntity event = mock(EventEntity.class);
when(eventLinkedCaseRepository.areAllAssociatedCasesAnonymised(event)).thenReturn(false);
assertThat(eventService.allAssociatedCasesAnonymised(event)).isFalse();
verify(eventLinkedCaseRepository).areAllAssociatedCasesAnonymised(event);
}
}