Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

We observed that a case family list is processed twice when a case ha… #689

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
88 changes: 54 additions & 34 deletions src/main/java/uk/gov/hmcts/reform/ccd/ApplicationExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.Set;
import java.util.stream.Collectors;

import static uk.gov.hmcts.reform.ccd.util.CaseFamilyUtil.FLATTEN_CASE_FAMILIES_FUNCTION;
Expand All @@ -35,56 +35,76 @@ public void execute() {
final List<CaseFamily> deletableCasesOnly = caseFamiliesFilter.getDeletableCasesOnly(caseFamiliesDueDeletion);
final List<CaseFamily> deletableLinkedFamiliesSimulation = caseFamiliesFilter
.geSimulationCasesOnly(caseFamiliesDueDeletion);

final List<CaseFamily> actuallyDeletableCases = new ArrayList<>();

final List<CaseData> flattenedCaseFamiliesView = FLATTEN_CASE_FAMILIES_FUNCTION.apply(deletableCasesOnly);

final List<CaseData> potentialMultiFamilyCases =
POTENTIAL_MULTI_FAMILY_CASE_AGGREGATOR_FUNCTION.apply(deletableCasesOnly);

Integer requestLimit = parameterResolver.getRequestLimit();

for (CaseData subjectCaseData : potentialMultiFamilyCases) {
final List<CaseFamily> linkedFamilies = findLinkedCaseFamilies(
final List<List<CaseData>> splitCaseFamily = POTENTIAL_MULTI_FAMILY_CASE_AGGREGATOR_FUNCTION.apply(
MartinYSpasov marked this conversation as resolved.
Show resolved Hide resolved
deletableCasesOnly);

for (List<CaseData> caseDataList : splitCaseFamily) {
final List<CaseFamily> deletableCaseFamilies = getCaseFamilies(
caseDataList,
flattenedCaseFamiliesView,
caseFamiliesDueDeletion,
subjectCaseData
caseFamiliesDueDeletion
);
final int linkedFamilySize = FLATTEN_CASE_FAMILIES_FUNCTION.apply(linkedFamilies).size();

// The RequestLimit specifies the total number of cases that can be deleted.
// In some scenarios, the linkedFamilySize may exceed the requestLimit, causing the deletion to be skipped.
// However, in other scenarios, if the linkedFamilySize is less than or equal to the requestLimit,
// the deletion will proceed.
if (requestLimit >= linkedFamilySize) {
caseDeletionService.deleteLinkedCaseFamilies(linkedFamilies);
actuallyDeletableCases.addAll(linkedFamilies);
requestLimit -= linkedFamilySize;

// If a root case has more than 1 child case, the linked family may be processed more than once.
// To avoid this, we check if the family id of the root case has already been processed.
final boolean hasNotBeenProcessed = isHasNotBeenProcessed(deletableCaseFamilies, actuallyDeletableCases);

if (hasNotBeenProcessed) {
final int linkedFamilySize = FLATTEN_CASE_FAMILIES_FUNCTION.apply(deletableCaseFamilies).size();

// The RequestLimit defines the maximum number of cases that can be deleted.
// If the linkedFamilySize exceeds this limit, the deletion will be skipped.
// However, if the linkedFamilySize is within the RequestLimit, the deletion will proceed.
if (requestLimit >= linkedFamilySize) {
caseDeletionService.deleteLinkedCaseFamilies(deletableCaseFamilies);
actuallyDeletableCases.addAll(deletableCaseFamilies);
requestLimit -= linkedFamilySize;
}
}
}

caseDeletionResolver.logCaseDeletion(actuallyDeletableCases, deletableLinkedFamiliesSimulation);
log.info("Case-Disposer finished.");
}

private List<CaseFamily> findLinkedCaseFamilies(final List<CaseData> flattenedCasesView,
final List<CaseFamily> allCaseFamilies,
final CaseData subjectCaseData) {
return buildLinkedFamilyIdsFunction(flattenedCasesView)
.andThen(buildLinkedFamiliesFunction(allCaseFamilies))
.apply(subjectCaseData);
private boolean isHasNotBeenProcessed(final List<CaseFamily> deletableCaseFamilies,
final List<CaseFamily> actuallyDeletableCases) {
final Set<Long> familyIdsForLinkedCases = deletableCaseFamilies.stream()
.map(caseFamily -> caseFamily.getRootCase().getFamilyId())
.collect(Collectors.toSet());

return actuallyDeletableCases.stream()
.noneMatch(caseFamily -> familyIdsForLinkedCases.contains(caseFamily.getRootCase().getFamilyId()));
}

private List<CaseFamily> getCaseFamilies(final List<CaseData> caseDataList,
final List<CaseData> flattenedCaseFamiliesView,
final List<CaseFamily> caseFamiliesDueDeletion) {
// Collect the family IDs of matching case data
final Set<Long> matchingFamilyIds = getMatchingFamilyIds(caseDataList, flattenedCaseFamiliesView);

// Filter case families due for deletion that have matching family IDs
return getMatchingCaseFamilies(caseFamiliesDueDeletion, matchingFamilyIds);
}

private Function<CaseData, List<Long>> buildLinkedFamilyIdsFunction(final List<CaseData> flattenedCasesView) {
return candidateCaseData -> flattenedCasesView.stream()
.filter(caseData -> caseData.getId().equals(candidateCaseData.getId()))
.map(CaseData::getFamilyId)
.collect(Collectors.toUnmodifiableList());
private Set<Long> getMatchingFamilyIds(List<CaseData> caseDataList, List<CaseData> flattenedCaseFamiliesView) {
return caseDataList.stream()
.flatMap(caseData -> flattenedCaseFamiliesView.stream()
.filter(flattenedCase -> flattenedCase.getId().equals(caseData.getId()))
.map(CaseData::getFamilyId))
.collect(Collectors.toSet());
}

private Function<List<Long>, List<CaseFamily>> buildLinkedFamiliesFunction(final List<CaseFamily> allCaseFamilies) {
return linkedCaseFamilyIds -> allCaseFamilies.stream()
.filter(caseFamily -> linkedCaseFamilyIds.contains(caseFamily.getRootCase().getFamilyId()))
.collect(Collectors.toUnmodifiableList());
private List<CaseFamily> getMatchingCaseFamilies(final List<CaseFamily> caseFamiliesDueDeletion,
final Set<Long> matchingFamilyIds) {
return caseFamiliesDueDeletion.stream()
.filter(caseFamily -> matchingFamilyIds.contains(caseFamily.getRootCase().getFamilyId()))
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import uk.gov.hmcts.reform.ccd.data.model.CaseFamily;
import uk.gov.hmcts.reform.ccd.service.remote.RemoteDisposeService;
import uk.gov.hmcts.reform.ccd.util.FailedToDeleteCaseFamilyHolder;
import uk.gov.hmcts.reform.ccd.util.Snooper;

import java.util.List;
import java.util.Optional;
Expand All @@ -29,7 +28,6 @@ public class CaseDeletionService {
private final CaseEventRepository caseEventRepository;
private final CaseLinkRepository caseLinkRepository;
private final RemoteDisposeService remoteDisposeService;
private final Snooper snooper;
private final FailedToDeleteCaseFamilyHolder failedToDeleteCaseFamilyHolder;


Expand All @@ -50,7 +48,7 @@ void deleteCase(final CaseFamily caseFamily) {
} catch (final Exception exception) { // Catch all exceptions
final String errorMessage = String.format("Could not delete case.reference:: %s",
rootCaseData.getReference());
snooper.snoop(errorMessage, exception);
log.error(errorMessage, exception);
failedToDeleteCaseFamilyHolder.addCaseFamily(caseFamily);
}
}
Expand All @@ -67,7 +65,7 @@ void deleteLinkedCases(final CaseFamily caseFamily) {
} catch (final Exception exception) { // Catch all exceptions
final String errorMessage = String.format("Could not delete linked case.reference:: %s",
caseData.getReference());
snooper.snoop(errorMessage, exception);
log.error(errorMessage, exception);
failedToDeleteCaseFamilyHolder.addCaseFamily(caseFamily);
}
});
Expand Down
49 changes: 24 additions & 25 deletions src/main/java/uk/gov/hmcts/reform/ccd/util/CaseFamilyUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,37 @@
import uk.gov.hmcts.reform.ccd.data.model.CaseFamily;

import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CaseFamilyUtil {

private CaseFamilyUtil() {
}

private static final BiFunction<List<CaseFamily>, Function<CaseFamily, List<CaseData>>, List<CaseData>>
AGGREGATE_FUNCTION = (caseFamilies, func) -> caseFamilies.stream()
.map(func)
.flatMap(List::stream)
.collect(Collectors.toUnmodifiableList());

public static final Function<List<CaseFamily>, List<CaseData>> FLATTEN_CASE_FAMILIES_FUNCTION = caseFamilies -> {
final Function<CaseFamily, List<CaseData>> func = caseFamily ->
Stream.of(List.of(caseFamily.getRootCase()), caseFamily.getLinkedCases())
.flatMap(List::stream)
.collect(Collectors.toUnmodifiableList());

return AGGREGATE_FUNCTION.apply(caseFamilies, func);
};

public static final Function<List<CaseFamily>, List<CaseData>> POTENTIAL_MULTI_FAMILY_CASE_AGGREGATOR_FUNCTION =
caseFamilies -> {
final Function<CaseFamily, List<CaseData>> func = caseFamily ->
caseFamily.getLinkedCases().isEmpty()
? List.of(caseFamily.getRootCase())
: caseFamily.getLinkedCases();

return AGGREGATE_FUNCTION.apply(caseFamilies, func);
public static final Function<List<CaseFamily>, List<CaseData>> FLATTEN_CASE_FAMILIES_FUNCTION = caseFamilies ->
caseFamilies.stream()
.flatMap(caseFamily ->
Stream.concat(Stream.of(caseFamily.getRootCase()), caseFamily.getLinkedCases().stream()))
.toList();

/**
* This function removes duplicate CaseData entries from the list of case families.
* Duplicate cases may occur when a case belongs to multiple root cases. This ensures
* that each case is processed only once, avoiding issues like deleting the same case family multiple times.
*/
public static final Function<List<CaseFamily>, List<List<CaseData>>>
MartinYSpasov marked this conversation as resolved.
Show resolved Hide resolved
POTENTIAL_MULTI_FAMILY_CASE_AGGREGATOR_FUNCTION = caseFamilies -> {
return caseFamilies.stream()
.map(caseFamily -> caseFamily.getLinkedCases().isEmpty()
? List.of(caseFamily.getRootCase()) // Wrap the root case in a list
: caseFamily.getLinkedCases()) // Return linked cases as a list
.toList();
};
}






19 changes: 0 additions & 19 deletions src/main/java/uk/gov/hmcts/reform/ccd/util/DefaultSnooper.java

This file was deleted.

7 changes: 0 additions & 7 deletions src/main/java/uk/gov/hmcts/reform/ccd/util/Snooper.java

This file was deleted.

Loading
Loading